Redistribuits els .cpp en carpetes

Actualitzat cmake
Modificats els include de SDL2 a SDL3
This commit is contained in:
2025-10-15 08:28:57 +02:00
parent c3415fd106
commit 78c5333144
86 changed files with 6757 additions and 7610 deletions

21
.clang-format Normal file
View File

@@ -0,0 +1,21 @@
BasedOnStyle: Google
IndentWidth: 4
IndentAccessModifiers: true
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: DontAlign
AlignAfterOpenBracket: DontAlign
BinPackArguments: false
BinPackParameters: false
ContinuationIndentWidth: 4
ConstructorInitializerIndentWidth: 4
IndentWrappedFunctionNames: false
Cpp11BracedListStyle: true
BreakConstructorInitializers: BeforeColon
AllowAllConstructorInitializersOnNextLine: false
PackConstructorInitializers: Never
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false

83
.clang-tidy Normal file
View File

@@ -0,0 +1,83 @@
Checks: >
readability-*,
modernize-*,
performance-*,
bugprone-unchecked-optional-access,
bugprone-sizeof-expression,
bugprone-suspicious-missing-comma,
bugprone-suspicious-index,
bugprone-undefined-memory-manipulation,
bugprone-use-after-move,
bugprone-out-of-bound-access,
-readability-identifier-length,
-readability-magic-numbers,
-bugprone-narrowing-conversions,
-performance-enum-size,
-performance-inefficient-string-concatenation,
-bugprone-integer-division,
-bugprone-easily-swappable-parameters,
WarningsAsErrors: '*'
# Solo incluir archivos de tu código fuente
HeaderFilterRegex: '^source/(sections|ui)/.*'
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: _ }
# Miembros protegidos en snake_case con sufijo _
- { key: readability-identifier-naming.ProtectedMemberCase, value: lower_case }
- { key: readability-identifier-naming.ProtectedMemberSuffix, value: _ }
# Miembros públicos en snake_case (sin sufijo)
- { key: readability-identifier-naming.PublicMemberCase, value: lower_case }
# Namespaces en CamelCase
- { key: readability-identifier-naming.NamespaceCase, value: CamelCase }
# Variables estáticas privadas como miembros privados
- { key: readability-identifier-naming.StaticVariableCase, value: lower_case }
- { key: readability-identifier-naming.StaticVariableSuffix, value: _ }
# Constantes estáticas sin sufijo
- { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE }
# Constantes globales en UPPER_CASE
- { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE }
# Variables constexpr globales en UPPER_CASE
- { key: readability-identifier-naming.ConstexprVariableCase, value: UPPER_CASE }
# Constantes locales en UPPER_CASE
- { key: readability-identifier-naming.LocalConstantCase, value: UPPER_CASE }
# Constexpr miembros en UPPER_CASE (sin sufijo)
- { key: readability-identifier-naming.ConstexprMemberCase, value: UPPER_CASE }
# Constexpr miembros privados/protegidos con sufijo _
- { key: readability-identifier-naming.ConstexprMethodCase, 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 (sin sufijos)
- { key: readability-identifier-naming.MethodCase, value: camelBack }
- { key: readability-identifier-naming.PrivateMethodCase, value: camelBack }
- { key: readability-identifier-naming.ProtectedMethodCase, value: camelBack }
- { key: readability-identifier-naming.PublicMethodCase, value: camelBack }
# Funciones en camelBack
- { key: readability-identifier-naming.FunctionCase, value: camelBack }
# Parámetros en lower_case
- { key: readability-identifier-naming.ParameterCase, value: lower_case }

View File

@@ -3,86 +3,128 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(jaildoctors_dilemma VERSION 1.00) project(jaildoctors_dilemma VERSION 1.00)
# Establece las políticas
cmake_policy(SET CMP0072 NEW)
# Configuración de compilador para MinGW en Windows, si es necesario
if(WIN32 AND NOT CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_COMPILER "g++")
set(CMAKE_C_COMPILER "gcc")
endif()
# Establecer estándar de C++ # Establecer estándar de C++
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
# Configuración global de flags de compilación # Establece la política CMP0072 para indicar cómo se debe seleccionar la implementación de OpenGL.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # En este caso, se elige la opción "GLVND", que utiliza bibliotecas modernas y modulares (libOpenGL, libGLX),
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -ffunction-sections -fdata-sections") # en lugar de la biblioteca OpenGL clásica (libGL). Esto mejora la compatibilidad con drivers recientes
# y evita ambigüedades cuando se encuentran múltiples implementaciones de OpenGL en el sistema.
cmake_policy(SET CMP0072 NEW)
set(OpenGL_GL_PREFERENCE GLVND)
# Define el directorio de los archivos fuente # --- GENERACIÓN DE VERSIÓN AUTOMÁTICA ---
set(DIR_SOURCES "${CMAKE_SOURCE_DIR}/source") find_package(Git QUIET)
if(GIT_FOUND)
# Cargar todos los archivos fuente en DIR_SOURCES execute_process(
file(GLOB SOURCES "${DIR_SOURCES}/*.cpp") COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
# Verificar si se encontraron archivos fuente OUTPUT_VARIABLE GIT_HASH
if(NOT SOURCES) OUTPUT_STRIP_TRAILING_WHITESPACE
message(FATAL_ERROR "No se encontraron archivos fuente en ${DIR_SOURCES}. Verifica que el directorio existe y contiene archivos .cpp.") ERROR_QUIET
endif() )
# Configuración de SDL2
find_package(SDL2 REQUIRED)
if(SDL2_FOUND)
message(STATUS "SDL2 encontrado: ${SDL2_INCLUDE_DIRS}")
include_directories(${SDL2_INCLUDE_DIRS})
link_directories(${SDL2_LIBDIR})
else() else()
message(FATAL_ERROR "SDL2 no encontrado") set(GIT_HASH "unknown")
endif() endif()
# Incluye rutas de SDL2 obtenidas con pkg-config # Configurar archivo de versión
include_directories(/usr/local/include /usr/local/include/SDL2) configure_file(${CMAKE_SOURCE_DIR}/source/version.h.in ${CMAKE_BINARY_DIR}/version.h @ONLY)
link_directories(/usr/local/lib)
# Definir las bibliotecas comunes # --- 1. LISTA EXPLÍCITA DE FUENTES ---
set(LIBS SDL2) set(APP_SOURCES
source/asset.cpp
source/cheevos.cpp
source/debug.cpp
source/director.cpp
source/enemy.cpp
source/gif.cpp
source/global_events.cpp
source/global_inputs.cpp
source/input.cpp
source/item_tracker.cpp
source/item.cpp
source/main.cpp
source/mouse.cpp
source/options.cpp
source/player.cpp
source/resource.cpp
source/room_tracker.cpp
source/room.cpp
source/scoreboard.cpp
source/screen.cpp
source/stats.cpp
source/surface.cpp
source/text.cpp
source/texture.cpp
source/utils.cpp
# Configuración común de salida de ejecutables en el directorio raíz source/ui/notifier.cpp
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
# Añadir ejecutable principal source/sections/credits.cpp
add_executable(${PROJECT_NAME} ${SOURCES}) source/sections/ending.cpp
source/sections/ending2.cpp
source/sections/game_over.cpp
source/sections/game.cpp
source/sections/loading_screen.cpp
source/sections/logo.cpp
source/sections/title.cpp
# Añadir definiciones de compilación dependiendo del tipo de build source/sprite/surface_animated_sprite.cpp
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<CONFIG:DEBUG>:DEBUG VERBOSE>) source/sprite/surface_moving_sprite.cpp
source/sprite/surface_sprite.cpp
# Enlazar bibliotecas source/external/jail_audio.cpp
target_link_libraries(${PROJECT_NAME} ${LIBS}) source/external/jail_shader.cpp
)
# Configuración de SDL3
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
# --- 2. AÑADIR EJECUTABLE ---
add_executable(${PROJECT_NAME} ${APP_SOURCES})
# --- 3. DIRECTORIOS DE INCLUSIÓN ---
target_include_directories(${PROJECT_NAME} PUBLIC
"${CMAKE_SOURCE_DIR}/source"
"${CMAKE_BINARY_DIR}"
)
# Enlazar la librería SDL3
target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3)
# --- 4. CONFIGURACIÓN PLATAFORMAS Y COMPILADOR ---
# Configuración de flags de compilación
target_compile_options(${PROJECT_NAME} PRIVATE -Wall)
target_compile_options(${PROJECT_NAME} PRIVATE $<$<CONFIG:RELEASE>:-Os -ffunction-sections -fdata-sections>)
# Definir _DEBUG en modo Debug
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<CONFIG:DEBUG>:_DEBUG>)
# Configuración específica para cada plataforma # Configuración específica para cada plataforma
if(WIN32) if(WIN32)
target_compile_definitions(${PROJECT_NAME} PRIVATE WINDOWS_BUILD) target_compile_definitions(${PROJECT_NAME} PRIVATE WINDOWS_BUILD)
target_link_libraries(${PROJECT_NAME} mingw32 opengl32 gdi32 winmm imm32 ole32 version) target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 mingw32 opengl32)
elseif(APPLE) elseif(APPLE)
set(LIBS ${LIBS} "-framework OpenGL")
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD) target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated") target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated)
# Configurar compilación para Apple Silicon
set(CMAKE_OSX_ARCHITECTURES "arm64") set(CMAKE_OSX_ARCHITECTURES "arm64")
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
set(LIBS ${LIBS} GL)
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD) target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
target_link_libraries(${PROJECT_NAME} ${LIBS})
endif() endif()
# Añadir OpenGL a las bibliotecas enlazadas # Configuración común para OpenGL
if(NOT WIN32) if(NOT WIN32)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
if(OPENGL_FOUND) if(OPENGL_FOUND)
message(STATUS "OpenGL encontrado: ${OPENGL_LIBRARIES}") message(STATUS "OpenGL encontrado: ${OPENGL_LIBRARIES}")
target_link_libraries(${PROJECT_NAME} ${OPENGL_LIBRARIES}) target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENGL_LIBRARIES})
else() else()
message(FATAL_ERROR "OpenGL no encontrado") message(FATAL_ERROR "OpenGL no encontrado")
endif() endif()
endif() endif()
# Especificar la ubicación del ejecutable
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})

View File

@@ -1,50 +1,47 @@
#include "cheevos.h" #include "cheevos.h"
#include <SDL2/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_rwops.h> // Para SDL_RWFromFile, SDL_RWclose, SDL_RWwrite #include <SDL3/SDL_error.h> // Para SDL_GetError
#include <SDL3/SDL_rwops.h> // Para SDL_RWFromFile, SDL_RWclose, SDL_RWwrite
#include <stddef.h> // Para NULL #include <stddef.h> // Para NULL
#include <fstream> // Para basic_ostream, operator<<, basic_ofstream #include <fstream> // Para basic_ostream, operator<<, basic_ofstream
#include <iostream> // Para cout, cerr #include <iostream> // Para cout, cerr
#include "notifier.h" // Para Notifier #include "notifier.h" // Para Notifier
#include "options.h" // Para Options, options #include "options.h" // Para Options, options
// [SINGLETON] // [SINGLETON]
Cheevos *Cheevos::cheevos_ = nullptr; Cheevos* Cheevos::cheevos_ = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
void Cheevos::init(const std::string &file) void Cheevos::init(const std::string& file) {
{
Cheevos::cheevos_ = new Cheevos(file); Cheevos::cheevos_ = new Cheevos(file);
} }
// [SINGLETON] Destruiremos el objeto con esta función estática // [SINGLETON] Destruiremos el objeto con esta función estática
void Cheevos::destroy() void Cheevos::destroy() {
{
delete Cheevos::cheevos_; delete Cheevos::cheevos_;
} }
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
Cheevos *Cheevos::get() Cheevos* Cheevos::get() {
{
return Cheevos::cheevos_; return Cheevos::cheevos_;
} }
// Constructor // Constructor
Cheevos::Cheevos(const std::string &file) Cheevos::Cheevos(const std::string& file)
: file_(file) : file_(file) {
{
init(); init();
loadFromFile(); loadFromFile();
} }
// Destructor // Destructor
Cheevos::~Cheevos() Cheevos::~Cheevos() {
{
saveToFile(); saveToFile();
} }
// Inicializa los logros // Inicializa los logros
void Cheevos::init() void Cheevos::init() {
{
cheevos_list_.clear(); cheevos_list_.clear();
cheevos_list_.emplace_back(1, "SHINY THINGS", "Get 25% of the items", 2); cheevos_list_.emplace_back(1, "SHINY THINGS", "Get 25% of the items", 2);
cheevos_list_.emplace_back(2, "HALF THE WORK", "Get 50% of the items", 2); cheevos_list_.emplace_back(2, "HALF THE WORK", "Get 50% of the items", 2);
@@ -61,12 +58,9 @@ void Cheevos::init()
} }
// Busca un logro por id y devuelve el indice // Busca un logro por id y devuelve el indice
int Cheevos::find(int id) int Cheevos::find(int id) {
{ for (int i = 0; i < (int)cheevos_list_.size(); ++i) {
for (int i = 0; i < (int)cheevos_list_.size(); ++i) if (cheevos_list_[i].id == id) {
{
if (cheevos_list_[i].id == id)
{
return i; return i;
} }
} }
@@ -75,13 +69,11 @@ int Cheevos::find(int id)
} }
// Desbloquea un logro // Desbloquea un logro
void Cheevos::unlock(int id) void Cheevos::unlock(int id) {
{
const int INDEX = find(id); const int INDEX = find(id);
// Si el índice es inválido, el logro no es válido, ya está completado o el sistema de logros no está habilitado, no hacemos nada // Si el índice es inválido, el logro no es válido, ya está completado o el sistema de logros no está habilitado, no hacemos nada
if (INDEX == -1 || !cheevos_list_.at(INDEX).obtainable || cheevos_list_.at(INDEX).completed || !enabled_) if (INDEX == -1 || !cheevos_list_.at(INDEX).obtainable || cheevos_list_.at(INDEX).completed || !enabled_) {
{
return; return;
} }
@@ -96,103 +88,80 @@ void Cheevos::unlock(int id)
} }
// Invalida un logro // Invalida un logro
void Cheevos::setUnobtainable(int id) void Cheevos::setUnobtainable(int id) {
{
const int index = find(id); const int index = find(id);
// Si el índice es válido, se invalida el logro // Si el índice es válido, se invalida el logro
if (index != -1) if (index != -1) {
{
cheevos_list_.at(index).obtainable = false; cheevos_list_.at(index).obtainable = false;
} }
} }
// Carga el estado de los logros desde un fichero // Carga el estado de los logros desde un fichero
void Cheevos::loadFromFile() void Cheevos::loadFromFile() {
{
std::ifstream file(file_, std::ios::binary); std::ifstream file(file_, std::ios::binary);
// El fichero no existe // El fichero no existe
if (!file) if (!file) {
{ if (options.console) {
if (options.console)
{
std::cout << "Warning: Unable to open " << file_ << "! Creating new file..." << std::endl; std::cout << "Warning: Unable to open " << file_ << "! Creating new file..." << std::endl;
} }
// Crea el fichero en modo escritura (binario) // Crea el fichero en modo escritura (binario)
std::ofstream newFile(file_, std::ios::binary); std::ofstream newFile(file_, std::ios::binary);
if (newFile) if (newFile) {
{ if (options.console) {
if (options.console)
{
std::cout << "New " << file_ << " created!" << std::endl; std::cout << "New " << file_ << " created!" << std::endl;
} }
// Guarda la información // Guarda la información
for (const auto &cheevo : cheevos_list_) for (const auto& cheevo : cheevos_list_) {
{ newFile.write(reinterpret_cast<const char*>(&cheevo.completed), sizeof(bool));
newFile.write(reinterpret_cast<const char *>(&cheevo.completed), sizeof(bool));
} }
} } else {
else if (options.console) {
{
if (options.console)
{
std::cerr << "Error: Unable to create " << file_ << "!" << std::endl; std::cerr << "Error: Unable to create " << file_ << "!" << std::endl;
} }
} }
} }
// El fichero existe // El fichero existe
else else {
{ if (options.console) {
if (options.console)
{
std::cout << "Reading " << file_ << std::endl; std::cout << "Reading " << file_ << std::endl;
} }
// Carga los datos // Carga los datos
for (auto &cheevo : cheevos_list_) for (auto& cheevo : cheevos_list_) {
{ file.read(reinterpret_cast<char*>(&cheevo.completed), sizeof(bool));
file.read(reinterpret_cast<char *>(&cheevo.completed), sizeof(bool));
} }
} }
} }
// Guarda el estado de los logros en un fichero // Guarda el estado de los logros en un fichero
void Cheevos::saveToFile() void Cheevos::saveToFile() {
{
// Abre el fichero en modo escritura (binario) // Abre el fichero en modo escritura (binario)
SDL_RWops *file = SDL_RWFromFile(this->file_.c_str(), "w+b"); SDL_RWops* file = SDL_RWFromFile(this->file_.c_str(), "w+b");
if (file != NULL) if (file != NULL) {
{
// Guarda la información // Guarda la información
for (int i = 0; i < (int)cheevos_list_.size(); ++i) for (int i = 0; i < (int)cheevos_list_.size(); ++i) {
{
SDL_RWwrite(file, &cheevos_list_[i].completed, sizeof(bool), 1); SDL_RWwrite(file, &cheevos_list_[i].completed, sizeof(bool), 1);
} }
// Cierra el fichero // Cierra el fichero
SDL_RWclose(file); SDL_RWclose(file);
} } else {
else if (options.console) {
{
if (options.console)
{
std::cout << "Error: Unable to save file! " << SDL_GetError() << std::endl; std::cout << "Error: Unable to save file! " << SDL_GetError() << std::endl;
} }
} }
} }
// Devuelve el número total de logros desbloqueados // Devuelve el número total de logros desbloqueados
int Cheevos::getTotalUnlockedAchievements() int Cheevos::getTotalUnlockedAchievements() {
{
int count = 0; int count = 0;
for (const auto &cheevo : cheevos_list_) for (const auto& cheevo : cheevos_list_) {
{ if (cheevo.completed) {
if (cheevo.completed)
{
count++; count++;
} }
} }
@@ -200,10 +169,8 @@ int Cheevos::getTotalUnlockedAchievements()
} }
// Elimina el estado "no obtenible" // Elimina el estado "no obtenible"
void Cheevos::clearUnobtainableState() void Cheevos::clearUnobtainableState() {
{ for (auto& cheevo : cheevos_list_) {
for (auto &cheevo : cheevos_list_)
{
cheevo.obtainable = true; cheevo.obtainable = true;
} }
} }

View File

@@ -1,279 +0,0 @@
#include "credits.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para min
#include "defines.h" // Para GAME_SPEED, PLAY_AREA_CENTER_X, PLAY_...
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "options.h" // Para Options, options, OptionsGame, Sectio...
#include "resource.h" // Para Resource
#include "s_animated_sprite.h" // Para SAnimatedSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "utils.h" // Para PaletteColor
// Constructor
Credits::Credits()
: shining_sprite_(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface("shine.gif"), Resource::get()->getAnimations("shine.ani")))
{
// Inicializa variables
options.section.section = Section::CREDITS;
options.section.subsection = Subsection::NONE;
shining_sprite_->setPos({194, 174, 8, 8});
// Cambia el color del borde
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
// Crea la textura para el texto que se escribe en pantalla
text_surface_ = std::make_shared<Surface>(options.game.width, options.game.height);
// Crea la textura para cubrir el rexto
cover_surface_ = std::make_shared<Surface>(options.game.width, options.game.height);
// Escribe el texto en la textura
fillTexture();
}
// Comprueba el manejador de eventos
void Credits::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
globalEvents::check(event);
}
}
// Comprueba las entradas
void Credits::checkInput()
{
globalInputs::check();
}
// Inicializa los textos
void Credits::iniTexts()
{
#ifndef GAME_CONSOLE
std::string keys = "";
switch (options.keys)
{
case ControlScheme::CURSOR:
keys = "CURSORS";
break;
case ControlScheme::OPQA:
keys = "O,P AND Q";
break;
case ControlScheme::WASD:
keys = "A,D AND W";
break;
default:
break;
}
texts_.clear();
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"INSTRUCTIONS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"HELP JAILDOC TO GET BACK ALL", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"HIS PROJECTS AND GO TO THE", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"JAIL TO FINISH THEM", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"KEYS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({keys + " TO MOVE AND JUMP", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"M TO SWITCH THE MUSIC", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"H TO PAUSE THE GAME", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"F1-F2 TO CHANGE WINDOWS SIZE", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"F3 TO SWITCH TO FULLSCREEN", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"B TO TOOGLE THE BORDER SCREEN", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"A GAME BY JAILDESIGNER", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"MADE ON SUMMER/FALL 2022", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"I LOVE JAILGAMES! ", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
#else
texts.clear();
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"INSTRUCTIONS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"HELP JAILDOC TO GET BACK ALL", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"HIS PROJECTS AND GO TO THE", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"JAIL TO FINISH THEM", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"KEYS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"B TO JUMP", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"R TO SWITCH THE MUSIC", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"L TO SWAP THE COLOR PALETTE", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"START TO PAUSE", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"SELECT TO EXIT", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"A GAME BY JAILDESIGNER", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"MADE ON SUMMER/FALL 2022", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"I LOVE JAILGAMES! ", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
#endif
}
// Escribe el texto en la textura
void Credits::fillTexture()
{
// Inicializa los textos
iniTexts();
// Rellena la textura de texto
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(text_surface_);
text_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
auto text = Resource::get()->getText("smb2");
// Escribe el texto en la textura
const int SIZE = text->getCharacterSize();
int pos_y = 0;
for (const auto &t : texts_)
{
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, pos_y * SIZE, t.label, 1, t.color);
pos_y++;
}
// Escribe el corazón
const int TEXT_LENGHT = text->lenght(texts_[22].label, 1) - text->lenght(" ", 1); // Se resta el ultimo caracter que es un espacio
const int POS_X = ((PLAY_AREA_WIDTH - TEXT_LENGHT) / 2) + TEXT_LENGHT;
text->writeColored(POS_X, 176, "}", static_cast<Uint8>(PaletteColor::BRIGHT_RED));
Screen::get()->setRendererSurface(previuos_renderer);
// Recoloca el sprite del brillo
shining_sprite_->setPosX(POS_X + 2);
// Rellena la textura que cubre el texto con color transparente
cover_surface_->clear(static_cast<Uint8>(PaletteColor::TRANSPARENT));
// Los primeros 8 pixels crea una malla
auto color = static_cast<Uint8>(PaletteColor::BLACK);
for (int i = 0; i < 256; i += 2)
{
cover_surface_->putPixel(i, 0, color);
cover_surface_->putPixel(i, 2, color);
cover_surface_->putPixel(i, 4, color);
cover_surface_->putPixel(i, 6, color);
cover_surface_->putPixel(i + 1, 5, color);
cover_surface_->putPixel(i + 1, 7, color);
}
// El resto se rellena de color sólido
SDL_Rect rect = {0, 8, 256, 192};
cover_surface_->fillRect(&rect, color);
}
// Actualiza el contador
void Credits::updateCounter()
{
// Incrementa el contador
if (counter_enabled_)
{
counter_++;
if (counter_ == 224 || counter_ == 544 || counter_ == 672)
{
counter_enabled_ = false;
}
}
else
{
sub_counter_++;
if (sub_counter_ == 100)
{
counter_enabled_ = true;
sub_counter_ = 0;
}
}
// Comprueba si ha terminado la sección
if (counter_ > 1200)
{
options.section.section = Section::DEMO;
}
}
// Actualiza las variables
void Credits::update()
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED)
{
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba las entradas
checkInput();
// Actualiza el contador
updateCounter();
Screen::get()->update();
// Actualiza el sprite con el brillo
if (counter_ > 770)
{
shining_sprite_->update();
}
}
}
// Dibuja en pantalla
void Credits::render()
{
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
// Limpia la pantalla
Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));
if (counter_ < 1150)
{
// Dibuja la textura con el texto en pantalla
text_surface_->render(0, 0);
// Dibuja la textura que cubre el texto
const int offset = std::min(counter_ / 8, 192 / 2);
SDL_Rect srcRect = {0, 0, 256, 192 - (offset * 2)};
cover_surface_->render(0, offset * 2, &srcRect);
// Dibuja el sprite con el brillo
shining_sprite_->render(1, static_cast<Uint8>(PaletteColor::BRIGHT_WHITE));
}
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void Credits::run()
{
while (options.section.section == Section::CREDITS)
{
update();
checkEvents();
render();
}
}

View File

@@ -1,61 +0,0 @@
#pragma once
#include <SDL2/SDL_stdinc.h> // Para Uint32, Uint8
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
class SAnimatedSprite; // lines 11-11
class Surface;
class Credits
{
private:
struct Captions
{
std::string label; // Texto a escribir
Uint8 color; // Color del texto
};
// Objetos y punteros
std::shared_ptr<Surface> text_surface_; // Textura para dibujar el texto
std::shared_ptr<Surface> cover_surface_; // Textura para cubrir el texto
std::shared_ptr<SAnimatedSprite> shining_sprite_; // Sprite para el brillo del corazón
// Variables
int counter_ = 0; // Contador
bool counter_enabled_ = true; // Indica si esta activo el contador
int sub_counter_ = 0; // Contador secundario
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<Captions> texts_; // Vector con los textos
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Actualiza el contador
void updateCounter();
// Inicializa los textos
void iniTexts();
// Escribe el texto en la textura
void fillTexture();
public:
// Constructor
Credits();
// Destructor
~Credits() = default;
// Bucle principal
void run();
};

View File

@@ -1,15 +1,15 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Point #include <SDL3/SDL_rect.h> // Para SDL_Point
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
// Clase Debug // Clase Debug
class Debug class Debug {
{ private:
private:
// [SINGLETON] Objeto privado // [SINGLETON] Objeto privado
static Debug *debug_; static Debug* debug_;
// Variables // Variables
std::vector<std::string> slot_; // Vector con los textos a escribir std::vector<std::string> slot_; // Vector con los textos a escribir
@@ -24,7 +24,7 @@ private:
// Destructor // Destructor
~Debug() = default; ~Debug() = default;
public: public:
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
static void init(); static void init();
@@ -32,7 +32,7 @@ public:
static void destroy(); static void destroy();
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
static Debug *get(); static Debug* get();
// Dibuja en pantalla // Dibuja en pantalla
void render(); void render();

View File

@@ -1,13 +1,15 @@
#pragma once #pragma once
#include <SDL2/SDL.h> #include <SDL3/SDL.h>
#include <string> #include <string>
#include "utils.h" #include "utils.h"
// Textos // Textos
constexpr const char *WINDOW_CAPTION = "JailDoctor's Dilemma"; constexpr const char* WINDOW_CAPTION = "JailDoctor's Dilemma";
constexpr const char *TEXT_COPYRIGHT = "@2022 JailDesigner"; constexpr const char* TEXT_COPYRIGHT = "@2022 JailDesigner";
constexpr const char *VERSION = "1.10"; constexpr const char* VERSION = "1.10";
// Velocidad del juego // Velocidad del juego
constexpr Uint32 GAME_SPEED = 15; constexpr Uint32 GAME_SPEED = 15;

View File

@@ -1,23 +1,26 @@
#include "director.h" #include "director.h"
#include <SDL2/SDL.h> // Para SDL_Init, SDL_Quit, SDL_INIT_EV...
#include <SDL2/SDL_audio.h> // Para AUDIO_S16 #include <SDL3/SDL.h> // Para SDL_Init, SDL_Quit, SDL_INIT_EV...
#include <SDL2/SDL_blendmode.h> // Para SDL_BLENDMODE_BLEND #include <SDL3/SDL_audio.h> // Para AUDIO_S16
#include <SDL2/SDL_error.h> // Para SDL_GetError #include <SDL3/SDL_blendmode.h> // Para SDL_BLENDMODE_BLEND
#include <SDL2/SDL_events.h> // Para SDL_DISABLE #include <SDL3/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_gamecontroller.h> // Para SDL_CONTROLLER_BUTTON_B, SDL_CO... #include <SDL3/SDL_events.h> // Para SDL_DISABLE
#include <SDL2/SDL_hints.h> // Para SDL_SetHint, SDL_HINT_RENDER_DR... #include <SDL3/SDL_gamecontroller.h> // Para SDL_CONTROLLER_BUTTON_B, SDL_CO...
#include <SDL2/SDL_mouse.h> // Para SDL_ShowCursor #include <SDL3/SDL_hints.h> // Para SDL_SetHint, SDL_HINT_RENDER_DR...
#include <SDL2/SDL_scancode.h> // Para SDL_SCANCODE_A, SDL_SCANCODE_ES... #include <SDL3/SDL_mouse.h> // Para SDL_ShowCursor
#include <SDL2/SDL_stdinc.h> // Para Uint32 #include <SDL3/SDL_scancode.h> // Para SDL_SCANCODE_A, SDL_SCANCODE_ES...
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_stdinc.h> // Para Uint32
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <errno.h> // Para errno, EEXIST, EACCES, ENAMETOO... #include <errno.h> // Para errno, EEXIST, EACCES, ENAMETOO...
#include <stdio.h> // Para printf, perror #include <stdio.h> // Para printf, perror
#include <sys/stat.h> // Para mkdir, stat, S_IRWXU #include <sys/stat.h> // Para mkdir, stat, S_IRWXU
#include <unistd.h> // Para getuid #include <unistd.h> // Para getuid
#include <cstdlib> // Para exit, EXIT_FAILURE, srand #include <cstdlib> // Para exit, EXIT_FAILURE, srand
#include <iostream> // Para basic_ostream, operator<<, cout #include <iostream> // Para basic_ostream, operator<<, cout
#include <memory> // Para make_unique, unique_ptr #include <memory> // Para make_unique, unique_ptr
#include <string> // Para operator+, allocator, char_traits #include <string> // Para operator+, allocator, char_traits
#include "asset.h" // Para Asset, AssetType #include "asset.h" // Para Asset, AssetType
#include "cheevos.h" // Para Cheevos #include "cheevos.h" // Para Cheevos
#include "credits.h" // Para Credits #include "credits.h" // Para Credits
@@ -42,8 +45,7 @@
#endif #endif
// Constructor // Constructor
Director::Director(int argc, const char *argv[]) Director::Director(int argc, const char* argv[]) {
{
std::cout << "Game start" << std::endl; std::cout << "Game start" << std::endl;
// Crea e inicializa las opciones del programa // Crea e inicializa las opciones del programa
@@ -60,8 +62,7 @@ Director::Director(int argc, const char *argv[])
createSystemFolder("jailgames/jaildoctors_dilemma"); createSystemFolder("jailgames/jaildoctors_dilemma");
// Si falta algún fichero no inicia el programa // Si falta algún fichero no inicia el programa
if (!setFileList()) if (!setFileList()) {
{
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -86,8 +87,7 @@ Director::Director(int argc, const char *argv[])
Cheevos::init(Asset::get()->get("cheevos.bin")); Cheevos::init(Asset::get()->get("cheevos.bin"));
} }
Director::~Director() Director::~Director() {
{
// Guarda las opciones a un fichero // Guarda las opciones a un fichero
saveOptionsToFile(Asset::get()->get("config.txt")); saveOptionsToFile(Asset::get()->get("config.txt"));
@@ -108,31 +108,20 @@ Director::~Director()
} }
// Comprueba los parametros del programa // Comprueba los parametros del programa
std::string Director::checkProgramArguments(int argc, const char *argv[]) std::string Director::checkProgramArguments(int argc, const char* argv[]) {
{
// Iterar sobre los argumentos del programa // Iterar sobre los argumentos del programa
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i) {
{
std::string argument(argv[i]); std::string argument(argv[i]);
if (argument == "--console") if (argument == "--console") {
{
options.console = true; options.console = true;
} } else if (argument == "--infiniteLives") {
else if (argument == "--infiniteLives")
{
options.cheats.infinite_lives = Cheat::CheatState::ENABLED; options.cheats.infinite_lives = Cheat::CheatState::ENABLED;
} } else if (argument == "--invincible") {
else if (argument == "--invincible")
{
options.cheats.invincible = Cheat::CheatState::ENABLED; options.cheats.invincible = Cheat::CheatState::ENABLED;
} } else if (argument == "--jailEnabled") {
else if (argument == "--jailEnabled")
{
options.cheats.jail_is_open = Cheat::CheatState::ENABLED; options.cheats.jail_is_open = Cheat::CheatState::ENABLED;
} } else if (argument == "--altSkin") {
else if (argument == "--altSkin")
{
options.cheats.alternate_skin = Cheat::CheatState::ENABLED; options.cheats.alternate_skin = Cheat::CheatState::ENABLED;
} }
} }
@@ -141,25 +130,23 @@ std::string Director::checkProgramArguments(int argc, const char *argv[])
} }
// Crea la carpeta del sistema donde guardar datos // Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(const std::string &folder) void Director::createSystemFolder(const std::string& folder) {
{
#ifdef _WIN32 #ifdef _WIN32
system_folder_ = std::string(getenv("APPDATA")) + "/" + folder; system_folder_ = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__ #elif __APPLE__
struct passwd *pw = getpwuid(getuid()); struct passwd* pw = getpwuid(getuid());
const char *homedir = pw->pw_dir; const char* homedir = pw->pw_dir;
system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder; system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder;
#elif __linux__ #elif __linux__
struct passwd *pw = getpwuid(getuid()); struct passwd* pw = getpwuid(getuid());
const char *homedir = pw->pw_dir; const char* homedir = pw->pw_dir;
system_folder_ = std::string(homedir) + "/.config/" + folder; system_folder_ = std::string(homedir) + "/.config/" + folder;
{ {
// Intenta crear ".config", per si no existeix // Intenta crear ".config", per si no existeix
std::string config_base_folder = std::string(homedir) + "/.config"; std::string config_base_folder = std::string(homedir) + "/.config";
int ret = mkdir(config_base_folder.c_str(), S_IRWXU); int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
if (ret == -1 && errno != EEXIST) if (ret == -1 && errno != EEXIST) {
{
printf("ERROR CREATING CONFIG BASE FOLDER."); printf("ERROR CREATING CONFIG BASE FOLDER.");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -167,8 +154,7 @@ void Director::createSystemFolder(const std::string &folder)
#endif #endif
struct stat st = {0}; struct stat st = {0};
if (stat(system_folder_.c_str(), &st) == -1) if (stat(system_folder_.c_str(), &st) == -1) {
{
errno = 0; errno = 0;
#ifdef _WIN32 #ifdef _WIN32
int ret = mkdir(system_folder_.c_str()); int ret = mkdir(system_folder_.c_str());
@@ -176,10 +162,8 @@ void Director::createSystemFolder(const std::string &folder)
int ret = mkdir(system_folder_.c_str(), S_IRWXU); int ret = mkdir(system_folder_.c_str(), S_IRWXU);
#endif #endif
if (ret == -1) if (ret == -1) {
{ switch (errno) {
switch (errno)
{
case EACCES: case EACCES:
printf("the parent directory does not allow write"); printf("the parent directory does not allow write");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -201,30 +185,24 @@ void Director::createSystemFolder(const std::string &folder)
} }
// Inicia las variables necesarias para arrancar el programa // Inicia las variables necesarias para arrancar el programa
void Director::initInput() void Director::initInput() {
{
// Busca si hay un mando conectado // Busca si hay un mando conectado
Input::get()->discoverGameControllers(); Input::get()->discoverGameControllers();
// Teclado - Movimiento // Teclado - Movimiento
if (options.keys == ControlScheme::CURSOR) if (options.keys == ControlScheme::CURSOR) {
{
Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_UP); Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_UP);
Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_LEFT); Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_LEFT);
Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_RIGHT); Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_RIGHT);
Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_UP); Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_UP);
Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_DOWN); Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_DOWN);
} } else if (options.keys == ControlScheme::OPQA) {
else if (options.keys == ControlScheme::OPQA)
{
Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_Q); Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_Q);
Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_O); Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_O);
Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_P); Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_P);
Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_Q); Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_Q);
Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_A); Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_A);
} } else if (options.keys == ControlScheme::WASD) {
else if (options.keys == ControlScheme::WASD)
{
Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_W); Input::get()->bindKey(InputAction::JUMP, SDL_SCANCODE_W);
Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_A); Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_A);
Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_D); Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_D);
@@ -250,8 +228,7 @@ void Director::initInput()
// MANDO // MANDO
const int NUM_GAMEPADS = Input::get()->getNumControllers(); const int NUM_GAMEPADS = Input::get()->getNumControllers();
for (int i = 0; i < NUM_GAMEPADS; ++i) for (int i = 0; i < NUM_GAMEPADS; ++i) {
{
// Movimiento // Movimiento
Input::get()->bindGameControllerButton(i, InputAction::JUMP, SDL_CONTROLLER_BUTTON_B); Input::get()->bindGameControllerButton(i, InputAction::JUMP, SDL_CONTROLLER_BUTTON_B);
Input::get()->bindGameControllerButton(i, InputAction::LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT); Input::get()->bindGameControllerButton(i, InputAction::LEFT, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
@@ -274,53 +251,41 @@ void Director::initInput()
} }
// Inicializa JailAudio // Inicializa JailAudio
void Director::initJailAudio() void Director::initJailAudio() {
{
JA_Init(48000, AUDIO_S16, 2); JA_Init(48000, AUDIO_S16, 2);
if (options.audio.enabled) if (options.audio.enabled) {
{
JA_SetMusicVolume(options.audio.music.volume); JA_SetMusicVolume(options.audio.music.volume);
JA_SetSoundVolume(options.audio.sound.volume); JA_SetSoundVolume(options.audio.sound.volume);
} } else {
else
{
JA_SetMusicVolume(0); JA_SetMusicVolume(0);
JA_SetSoundVolume(0); JA_SetSoundVolume(0);
} }
} }
// Arranca SDL y crea la ventana // Arranca SDL y crea la ventana
bool Director::initSDL() bool Director::initSDL() {
{
// Indicador de éxito // Indicador de éxito
bool success = true; bool success = true;
// Inicializa SDL // Inicializa SDL
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
{ if (options.console) {
if (options.console)
{
std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl; std::cout << "SDL could not initialize!\nSDL Error: " << SDL_GetError() << std::endl;
} }
success = false; success = false;
} } else {
else
{
// Inicia el generador de numeros aleatorios // Inicia el generador de numeros aleatorios
std::srand(static_cast<unsigned int>(SDL_GetTicks())); std::srand(static_cast<unsigned int>(SDL_GetTicks()));
// Establece el filtro de la textura a nearest // Establece el filtro de la textura a nearest
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, std::to_string(static_cast<int>(options.video.filter)).c_str())) if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, std::to_string(static_cast<int>(options.video.filter)).c_str())) {
{ if (options.console) {
if (options.console)
{
std::cout << "Warning: Nearest texture filtering not enabled!\n"; std::cout << "Warning: Nearest texture filtering not enabled!\n";
} }
} }
// Activa el render OpenGL // Activa el render OpenGL
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
{
std::cout << "Warning: opengl not enabled!\n"; std::cout << "Warning: opengl not enabled!\n";
} }
@@ -329,34 +294,25 @@ bool Director::initSDL()
const auto window_height = options.video.border.enabled ? options.game.height + options.video.border.height * 2 : options.game.height; const auto window_height = options.video.border.enabled ? options.game.height + options.video.border.height * 2 : options.game.height;
window_ = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width * options.window.zoom, window_height * options.window.zoom, SDL_WINDOW_HIDDEN); window_ = SDL_CreateWindow(WINDOW_CAPTION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width * options.window.zoom, window_height * options.window.zoom, SDL_WINDOW_HIDDEN);
if (window_ == nullptr) if (window_ == nullptr) {
{ if (options.console) {
if (options.console)
{
std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl; std::cout << "Window could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
} }
success = false; success = false;
} } else {
else
{
// Crea un renderizador para la ventana. El vsync se activa en funcion de las opciones // Crea un renderizador para la ventana. El vsync se activa en funcion de las opciones
Uint32 flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE; Uint32 flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE;
if (options.video.vertical_sync) if (options.video.vertical_sync) {
{
flags = flags | SDL_RENDERER_PRESENTVSYNC; flags = flags | SDL_RENDERER_PRESENTVSYNC;
} }
renderer_ = SDL_CreateRenderer(window_, -1, flags); renderer_ = SDL_CreateRenderer(window_, -1, flags);
if (renderer_ == nullptr) if (renderer_ == nullptr) {
{ if (options.console) {
if (options.console)
{
std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl; std::cout << "Renderer could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
} }
success = false; success = false;
} } else {
else
{
// Inicializa el color de renderizado // Inicializa el color de renderizado
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF); SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
@@ -371,16 +327,14 @@ bool Director::initSDL()
} }
} }
if (options.console) if (options.console) {
{
std::cout << std::endl; std::cout << std::endl;
} }
return success; return success;
} }
// Crea el indice de ficheros // Crea el indice de ficheros
bool Director::setFileList() bool Director::setFileList() {
{
#ifdef MACOS_BUNDLE #ifdef MACOS_BUNDLE
const std::string prefix = "/../Resources"; const std::string prefix = "/../Resources";
#else #else
@@ -429,8 +383,7 @@ bool Director::setFileList()
Asset::get()->add(system_folder_ + "/cheevos.bin", AssetType::DATA, false, true); Asset::get()->add(system_folder_ + "/cheevos.bin", AssetType::DATA, false, true);
// Tilemaps y Rooms // Tilemaps y Rooms
for (int i = 1; i <= 60; ++i) for (int i = 1; i <= 60; ++i) {
{
std::string index = (i < 10 ? "0" : "") + std::to_string(i); std::string index = (i < 10 ? "0" : "") + std::to_string(i);
Asset::get()->add(prefix + "/data/room/" + index + ".tmx", AssetType::TILEMAP); Asset::get()->add(prefix + "/data/room/" + index + ".tmx", AssetType::TILEMAP);
Asset::get()->add(prefix + "/data/room/" + index + ".room", AssetType::ROOM); Asset::get()->add(prefix + "/data/room/" + index + ".room", AssetType::ROOM);
@@ -582,8 +535,7 @@ bool Director::setFileList()
Asset::get()->add(prefix + "/data/sound/notify.wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/notify.wav", AssetType::SOUND);
// Efectos de sonido para el salto // Efectos de sonido para el salto
for (int i = 1; i <= 24; ++i) for (int i = 1; i <= 24; ++i) {
{
std::string jump_index = std::to_string(i); std::string jump_index = std::to_string(i);
Asset::get()->add(prefix + "/data/sound/jump" + jump_index + ".wav", AssetType::SOUND); Asset::get()->add(prefix + "/data/sound/jump" + jump_index + ".wav", AssetType::SOUND);
} }
@@ -614,76 +566,64 @@ bool Director::setFileList()
} }
// Ejecuta la seccion de juego con el logo // Ejecuta la seccion de juego con el logo
void Director::runLogo() void Director::runLogo() {
{
auto logo = std::make_unique<Logo>(); auto logo = std::make_unique<Logo>();
logo->run(); logo->run();
} }
// Ejecuta la seccion de juego de la pantalla de carga // Ejecuta la seccion de juego de la pantalla de carga
void Director::runLoadingScreen() void Director::runLoadingScreen() {
{
auto loadingScreen = std::make_unique<LoadingScreen>(); auto loadingScreen = std::make_unique<LoadingScreen>();
loadingScreen->run(); loadingScreen->run();
} }
// Ejecuta la seccion de juego con el titulo y los menus // Ejecuta la seccion de juego con el titulo y los menus
void Director::runTitle() void Director::runTitle() {
{
auto title = std::make_unique<Title>(); auto title = std::make_unique<Title>();
title->run(); title->run();
} }
// Ejecuta la seccion de los creditos del juego // Ejecuta la seccion de los creditos del juego
void Director::runCredits() void Director::runCredits() {
{
auto credits = std::make_unique<Credits>(); auto credits = std::make_unique<Credits>();
credits->run(); credits->run();
} }
// Ejecuta la seccion de la demo, donde se ven pantallas del juego // Ejecuta la seccion de la demo, donde se ven pantallas del juego
void Director::runDemo() void Director::runDemo() {
{
auto game = std::make_unique<Game>(GameMode::DEMO); auto game = std::make_unique<Game>(GameMode::DEMO);
game->run(); game->run();
} }
// Ejecuta la seccion del final del juego // Ejecuta la seccion del final del juego
void Director::runEnding() void Director::runEnding() {
{
auto ending = std::make_unique<Ending>(); auto ending = std::make_unique<Ending>();
ending->run(); ending->run();
} }
// Ejecuta la seccion del final del juego // Ejecuta la seccion del final del juego
void Director::runEnding2() void Director::runEnding2() {
{
auto ending2 = std::make_unique<Ending2>(); auto ending2 = std::make_unique<Ending2>();
ending2->run(); ending2->run();
} }
// Ejecuta la seccion del final de la partida // Ejecuta la seccion del final de la partida
void Director::runGameOver() void Director::runGameOver() {
{
auto gameOver = std::make_unique<GameOver>(); auto gameOver = std::make_unique<GameOver>();
gameOver->run(); gameOver->run();
} }
// Ejecuta la seccion de juego donde se juega // Ejecuta la seccion de juego donde se juega
void Director::runGame() void Director::runGame() {
{
JA_StopMusic(); JA_StopMusic();
auto game = std::make_unique<Game>(GameMode::GAME); auto game = std::make_unique<Game>(GameMode::GAME);
game->run(); game->run();
} }
int Director::run() int Director::run() {
{
// Bucle principal // Bucle principal
while (options.section.section != Section::QUIT) while (options.section.section != Section::QUIT) {
{ switch (options.section.section) {
switch (options.section.section)
{
case Section::LOGO: case Section::LOGO:
runLogo(); runLogo();
break; break;

View File

@@ -1,25 +1,25 @@
#pragma once #pragma once
#include <SDL2/SDL_render.h> // Para SDL_Renderer #include <SDL3/SDL_render.h> // Para SDL_Renderer
#include <SDL2/SDL_video.h> // Para SDL_Window #include <SDL3/SDL_video.h> // Para SDL_Window
#include <string> // Para string #include <string> // Para string
class Director class Director {
{ private:
private:
// Objetos y punteros // Objetos y punteros
SDL_Window *window_; // La ventana donde dibujamos SDL_Window* window_; // La ventana donde dibujamos
SDL_Renderer *renderer_; // El renderizador de la ventana SDL_Renderer* renderer_; // El renderizador de la ventana
// Variables // Variables
std::string executable_path_; // Path del ejecutable std::string executable_path_; // Path del ejecutable
std::string system_folder_; // Carpeta del sistema donde guardar datos std::string system_folder_; // Carpeta del sistema donde guardar datos
// Comprueba los parametros del programa // Comprueba los parametros del programa
std::string checkProgramArguments(int argc, const char *argv[]); std::string checkProgramArguments(int argc, const char* argv[]);
// Crea la carpeta del sistema donde guardar datos // Crea la carpeta del sistema donde guardar datos
void createSystemFolder(const std::string &folder); void createSystemFolder(const std::string& folder);
// Inicializa jail_audio // Inicializa jail_audio
void initJailAudio(); void initJailAudio();
@@ -60,9 +60,9 @@ private:
// Ejecuta la seccion de juego donde se juega // Ejecuta la seccion de juego donde se juega
void runGame(); void runGame();
public: public:
// Constructor // Constructor
Director(int argc, const char *argv[]); Director(int argc, const char* argv[]);
// Destructor // Destructor
~Director(); ~Director();

View File

@@ -1,104 +0,0 @@
#pragma once
#include <SDL2/SDL_stdinc.h> // Para Uint32
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
class SSprite; // lines 8-8
class Surface; // lines 9-9
class Ending
{
private:
// Estructuras
struct EndingSurface // Estructura con dos texturas y sprites, uno para mostrar y el otro hace de cortinilla
{
std::shared_ptr<Surface> image_surface; // Surface a mostrar
std::shared_ptr<SSprite> image_sprite; // SSprite para mostrar la textura
std::shared_ptr<Surface> cover_surface; // Surface que cubre a la otra textura
std::shared_ptr<SSprite> cover_sprite; // SSprite para mostrar la textura que cubre a la otra textura
int cover_clip_desp; // Desplazamiento del spriteClip de la textura de cobertura
int cover_clip_height; // Altura del spriteClip de la textura de cobertura
};
struct TextAndPosition // Estructura con un texto y su posición en el eje Y
{
std::string caption; // Texto
int pos; // Posición
};
struct TextIndex
{
int index;
int trigger;
};
struct SceneData // Estructura para crear cada una de las escenas del final
{
std::vector<TextIndex> text_index; // Indices del vector de textos a mostrar y su disparador
int picture_index; // Indice del vector de imagenes a mostrar
int counter_end; // Valor del contador en el que finaliza la escena
};
// Objetos y punteros
std::shared_ptr<Surface> cover_surface_; // Surface para cubrir el texto
// Variables
int counter_; // Contador
int pre_counter_; // Contador previo
int cover_counter_; // Contador para la cortinilla
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa
std::vector<EndingSurface> sprite_texts_; // Vector con los sprites de texto con su cortinilla
std::vector<EndingSurface> sprite_pics_; // Vector con los sprites de texto con su cortinilla
int current_scene_; // Escena actual
std::vector<SceneData> scenes_; // Vector con los textos e imagenes de cada escena
// Actualiza el objeto
void update();
// Dibuja el final en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Inicializa los textos
void iniTexts();
// Inicializa las imagenes
void iniPics();
// Inicializa las escenas
void iniScenes();
// Actualiza los contadores
void updateCounters();
// Actualiza las cortinillas de los elementos
void updateSpriteCovers();
// Comprueba si se ha de cambiar de escena
void checkChangeScene();
// Rellena la textura para la cortinilla
void fillCoverTexture();
// Dibuja la cortinilla de cambio de escena
void renderCoverTexture();
// Actualiza el volumen de la musica
void updateMusicVolume();
public:
// Constructor
Ending();
// Destructor
~Ending() = default;
// Bucle principal
void run();
};

View File

@@ -1,142 +0,0 @@
#pragma once
#include <SDL2/SDL_stdinc.h> // Para Uint32, Uint8
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "defines.h" // Para GAMECANVAS_WIDTH, GAMECANVAS_FIRST_QUAR...
class SAnimatedSprite; // lines 9-9
class SMovingSprite; // lines 10-10
class Ending2
{
private:
// Enum para representar los estados del final
enum class EndingState : int
{
PRE_CREDITS, // Estado previo a los créditos
CREDITS, // Estado de los créditos
POST_CREDITS, // Estado posterior a los créditos
FADING, // Estado de fundido de los textos a negrp
};
// Estructura para controlar los estados y su duración
struct State
{
EndingState state; // Estado actual
Uint32 init_ticks; // Ticks en los que se inicializó el estado
Uint32 duration; // Duración en milisegundos para el estado actual
// Constructor parametrizado para inicializar la estructura
State(EndingState initialState, Uint32 initialTicks, Uint32 stateDuration)
: state(initialState), init_ticks(initialTicks), duration(stateDuration) {}
// Método para comprobar si el estado ha terminado y verifica el nombre del estado
bool hasEnded(EndingState expectedState) const
{
// Comprobar si el estado actual coincide con el estado esperado
if (state != expectedState)
{
return false; // Si no coincide, considerar que no ha terminado
}
// Comprobar si el tiempo transcurrido excede la duración
return (SDL_GetTicks() - init_ticks) >= duration;
}
// Método para establecer un nuevo estado
void set(EndingState newState, Uint32 newDuration)
{
state = newState; // Actualizar el estado
init_ticks = SDL_GetTicks(); // Reiniciar el tiempo de inicio
duration = newDuration; // Actualizar la duración
}
};
// Constantes
static constexpr int FIRST_COL_ = GAMECANVAS_FIRST_QUARTER_X + (GAMECANVAS_WIDTH / 16); // Primera columna por donde desfilan los sprites
static constexpr int SECOND_COL_ = GAMECANVAS_THIRD_QUARTER_X - (GAMECANVAS_WIDTH / 16); // Segunda columna por donde desfilan los sprites
static constexpr int DIST_SPRITE_TEXT_ = 8; // Distancia entre el sprite y el texto que lo acompaña
static constexpr int DIST_SPRITE_SPRITE_ = 0; // Distancia entre dos sprites de la misma columna
static constexpr float SPRITE_DESP_SPEED_ = -0.2f; // Velocidad de desplazamiento de los sprites
static constexpr int STATE_PRE_CREDITS_DURATION_ = 3000;
static constexpr int STATE_POST_CREDITS_DURATION_ = 5000;
static constexpr int STATE_FADE_DURATION_ = 5000;
// Objetos y punteros
std::vector<std::shared_ptr<SAnimatedSprite>> sprites_; // Vector con todos los sprites a dibujar
std::vector<std::shared_ptr<SMovingSprite>> sprite_texts_; // Vector con los sprites de texto de los sprites
std::vector<std::shared_ptr<SMovingSprite>> texts_; // Vector con los sprites de texto
// Variables
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<std::string> sprite_list_; // Lista con todos los sprites a dibujar
std::vector<Uint8> colors_; // Vector con los colores para el fade
int sprite_max_width_ = 0; // El valor de ancho del sprite mas ancho
int sprite_max_height_ = 0; // El valor de alto del sprite mas alto
State state_; // Controla el estado de la clase
// Actualiza el objeto
void update();
// Dibuja el final en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Actualiza el estado
void updateState();
// Inicializa la lista de sprites
void iniSpriteList();
// Carga todos los sprites desde una lista
void loadSprites();
// Actualiza los sprites
void updateSprites();
// Actualiza los sprites de texto
void updateTextSprites();
// Actualiza los sprites de texto del final
void updateTexts();
// Dibuja los sprites
void renderSprites();
// Dibuja los sprites con el texto
void renderSpriteTexts();
// Dibuja los sprites con el texto del final
void renderTexts();
// Coloca los sprites en su sito
void placeSprites();
// Crea los sprites con las texturas con los textos
void createSpriteTexts();
// Crea los sprites con las texturas con los textos del final
void createTexts();
// Actualiza el fade final
void updateFinalFade();
// Actualiza el volumen de la musica
void updateMusicVolume();
public:
// Constructor
Ending2();
// Destructor
~Ending2() = default;
// Bucle principal
void run();
};

View File

@@ -1,12 +1,14 @@
#include "enemy.h" #include "enemy.h"
#include <SDL2/SDL_render.h> // Para SDL_RendererFlip, SDL_FLIP_NONE, SDL_...
#include <SDL3/SDL_render.h> // Para SDL_RendererFlip, SDL_FLIP_NONE, SDL_...
#include <stdlib.h> // Para rand #include <stdlib.h> // Para rand
#include "resource.h" // Para Resource #include "resource.h" // Para Resource
#include "s_animated_sprite.h" // Para SAnimatedSprite #include "s_animated_sprite.h" // Para SAnimatedSprite
#include "utils.h" // Para stringToColor #include "utils.h" // Para stringToColor
// Constructor // Constructor
Enemy::Enemy(const EnemyData &enemy) Enemy::Enemy(const EnemyData& enemy)
: sprite_(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface(enemy.surface_path), Resource::get()->getAnimations(enemy.animation_path))), : sprite_(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface(enemy.surface_path), Resource::get()->getAnimations(enemy.animation_path))),
color_string_(enemy.color), color_string_(enemy.color),
x1_(enemy.x1), x1_(enemy.x1),
@@ -14,8 +16,7 @@ Enemy::Enemy(const EnemyData &enemy)
y1_(enemy.y1), y1_(enemy.y1),
y2_(enemy.y2), y2_(enemy.y2),
should_flip_(enemy.flip), should_flip_(enemy.flip),
should_mirror_(enemy.mirror) should_mirror_(enemy.mirror) {
{
// Obten el resto de valores // Obten el resto de valores
sprite_->setPosX(enemy.x); sprite_->setPosX(enemy.x);
sprite_->setPosY(enemy.y); sprite_->setPosY(enemy.y);
@@ -37,31 +38,24 @@ Enemy::Enemy(const EnemyData &enemy)
} }
// Pinta el enemigo en pantalla // Pinta el enemigo en pantalla
void Enemy::render() void Enemy::render() {
{
sprite_->render(1, color_); sprite_->render(1, color_);
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Enemy::update() void Enemy::update() {
{
sprite_->update(); sprite_->update();
checkPath(); checkPath();
collider_ = getRect(); collider_ = getRect();
} }
// Comprueba si ha llegado al limite del recorrido para darse media vuelta // Comprueba si ha llegado al limite del recorrido para darse media vuelta
void Enemy::checkPath() void Enemy::checkPath() {
{ if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_) {
if (sprite_->getPosX() > x2_ || sprite_->getPosX() < x1_)
{
// Recoloca // Recoloca
if (sprite_->getPosX() > x2_) if (sprite_->getPosX() > x2_) {
{
sprite_->setPosX(x2_); sprite_->setPosX(x2_);
} } else {
else
{
sprite_->setPosX(x1_); sprite_->setPosX(x1_);
} }
@@ -69,21 +63,16 @@ void Enemy::checkPath()
sprite_->setVelX(sprite_->getVelX() * (-1)); sprite_->setVelX(sprite_->getVelX() * (-1));
// Invierte el sprite // Invierte el sprite
if (should_flip_) if (should_flip_) {
{
sprite_->flip(); sprite_->flip();
} }
} }
if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) if (sprite_->getPosY() > y2_ || sprite_->getPosY() < y1_) {
{
// Recoloca // Recoloca
if (sprite_->getPosY() > y2_) if (sprite_->getPosY() > y2_) {
{
sprite_->setPosY(y2_); sprite_->setPosY(y2_);
} } else {
else
{
sprite_->setPosY(y1_); sprite_->setPosY(y1_);
} }
@@ -91,21 +80,18 @@ void Enemy::checkPath()
sprite_->setVelY(sprite_->getVelY() * (-1)); sprite_->setVelY(sprite_->getVelY() * (-1));
// Invierte el sprite // Invierte el sprite
if (should_flip_) if (should_flip_) {
{
sprite_->flip(); sprite_->flip();
} }
} }
} }
// Devuelve el rectangulo que contiene al enemigo // Devuelve el rectangulo que contiene al enemigo
SDL_Rect Enemy::getRect() SDL_Rect Enemy::getRect() {
{
return sprite_->getRect(); return sprite_->getRect();
} }
// Obtiene el rectangulo de colision del enemigo // Obtiene el rectangulo de colision del enemigo
SDL_Rect &Enemy::getCollider() SDL_Rect& Enemy::getCollider() {
{
return collider_; return collider_;
} }

View File

@@ -1,14 +1,14 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
class SAnimatedSprite; // lines 7-7 class SAnimatedSprite; // lines 7-7
// Estructura para pasar los datos de un enemigo // Estructura para pasar los datos de un enemigo
struct EnemyData struct EnemyData {
{
std::string surface_path; // Ruta al fichero con la textura std::string surface_path; // Ruta al fichero con la textura
std::string animation_path; // Ruta al fichero con la animación std::string animation_path; // Ruta al fichero con la animación
int w; // Anchura del enemigo int w; // Anchura del enemigo
@@ -27,9 +27,8 @@ struct EnemyData
std::string color; // Color del enemigo std::string color; // Color del enemigo
}; };
class Enemy class Enemy {
{ private:
private:
// Objetos y punteros // Objetos y punteros
std::shared_ptr<SAnimatedSprite> sprite_; // Sprite del enemigo std::shared_ptr<SAnimatedSprite> sprite_; // Sprite del enemigo
@@ -47,9 +46,9 @@ private:
// Comprueba si ha llegado al limite del recorrido para darse media vuelta // Comprueba si ha llegado al limite del recorrido para darse media vuelta
void checkPath(); void checkPath();
public: public:
// Constructor // Constructor
explicit Enemy(const EnemyData &enemy); explicit Enemy(const EnemyData& enemy);
// Destructor // Destructor
~Enemy() = default; ~Enemy() = default;
@@ -64,5 +63,5 @@ public:
SDL_Rect getRect(); SDL_Rect getRect();
// Obtiene el rectangulo de colision del enemigo // Obtiene el rectangulo de colision del enemigo
SDL_Rect &getCollider(); SDL_Rect& getCollider();
}; };

2
source/external/.clang-format vendored Normal file
View File

@@ -0,0 +1,2 @@
DisableFormat: true
SortIncludes: Never

4
source/external/.clang-tidy vendored Normal file
View File

@@ -0,0 +1,4 @@
# source/external/.clang-tidy
Checks: '-*'
WarningsAsErrors: ''
HeaderFilterRegex: ''

View File

@@ -1,38 +1,37 @@
#include "jail_audio.h" #include "jail_audio.h"
#include <SDL2/SDL_rwops.h> // Para SDL_RWFromMem
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_rwops.h> // Para SDL_RWFromMem
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <stdint.h> // Para uint8_t, uint32_t #include <stdint.h> // Para uint8_t, uint32_t
#include <stdio.h> // Para NULL, fseek, fclose, fopen, fread, ftell #include <stdio.h> // Para NULL, fseek, fclose, fopen, fread, ftell
#include <stdlib.h> // Para free, malloc #include <stdlib.h> // Para free, malloc
#include "stb_vorbis.c" // Para stb_vorbis_decode_memory #include "stb_vorbis.c" // Para stb_vorbis_decode_memory
constexpr int JA_MAX_SIMULTANEOUS_CHANNELS = 20; constexpr int JA_MAX_SIMULTANEOUS_CHANNELS = 20;
struct JA_Sound_t struct JA_Sound_t {
{
Uint32 length{0}; Uint32 length{0};
Uint8 *buffer{NULL}; Uint8* buffer{NULL};
}; };
struct JA_Channel_t struct JA_Channel_t {
{ JA_Sound_t* sound;
JA_Sound_t *sound;
int pos{0}; int pos{0};
int times{0}; int times{0};
JA_Channel_state state{JA_CHANNEL_FREE}; JA_Channel_state state{JA_CHANNEL_FREE};
}; };
struct JA_Music_t struct JA_Music_t {
{
int samples{0}; int samples{0};
Uint32 length{0}; Uint32 length{0};
int pos{0}; int pos{0};
int times{0}; int times{0};
short *output{NULL}; short* output{NULL};
JA_Music_state state{JA_MUSIC_INVALID}; JA_Music_state state{JA_MUSIC_INVALID};
}; };
JA_Music_t *current_music{NULL}; JA_Music_t* current_music{NULL};
JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS]; JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
int JA_freq{48000}; int JA_freq{48000};
@@ -49,67 +48,51 @@ int fade_start_time;
int fade_duration; int fade_duration;
int fade_initial_volume; int fade_initial_volume;
void audioCallback(void *userdata, uint8_t *stream, int len) void audioCallback(void* userdata, uint8_t* stream, int len) {
{
SDL_memset(stream, 0, len); SDL_memset(stream, 0, len);
if (current_music != NULL && current_music->state == JA_MUSIC_PLAYING) if (current_music != NULL && current_music->state == JA_MUSIC_PLAYING) {
{
int volume = JA_musicVolume; int volume = JA_musicVolume;
if (fading) if (fading) {
{
int time = SDL_GetTicks(); int time = SDL_GetTicks();
if (time > (fade_start_time + fade_duration)) if (time > (fade_start_time + fade_duration)) {
{
fading = false; fading = false;
current_music->pos = 0; current_music->pos = 0;
current_music->state = JA_MUSIC_STOPPED; current_music->state = JA_MUSIC_STOPPED;
volume = 0; volume = 0;
} } else {
else
{
const int time_passed = time - fade_start_time; const int time_passed = time - fade_start_time;
const float percent = (float)time_passed / (float)fade_duration; const float percent = (float)time_passed / (float)fade_duration;
volume = JA_musicVolume * (1.0 - percent); volume = JA_musicVolume * (1.0 - percent);
} }
} }
const int size = SDL_min(len, current_music->length - current_music->pos); const int size = SDL_min(len, current_music->length - current_music->pos);
SDL_MixAudioFormat(stream, (Uint8 *)(current_music->output) + current_music->pos, AUDIO_S16, size, volume); SDL_MixAudioFormat(stream, (Uint8*)(current_music->output) + current_music->pos, AUDIO_S16, size, volume);
current_music->pos += size; current_music->pos += size;
if (size < len) if (size < len) {
{ if (current_music->times != 0) {
if (current_music->times != 0) SDL_MixAudioFormat(stream + size, (Uint8*)current_music->output, AUDIO_S16, len - size, volume);
{
SDL_MixAudioFormat(stream + size, (Uint8 *)current_music->output, AUDIO_S16, len - size, volume);
current_music->pos = len - size; current_music->pos = len - size;
if (current_music->times > 0) if (current_music->times > 0)
current_music->times--; current_music->times--;
} } else {
else
{
current_music->pos = 0; current_music->pos = 0;
current_music->state = JA_MUSIC_STOPPED; current_music->state = JA_MUSIC_STOPPED;
} }
} }
} }
// Mixar els channels mi amol // Mixar els channels mi amol
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
{ if (channels[i].state == JA_CHANNEL_PLAYING) {
if (channels[i].state == JA_CHANNEL_PLAYING)
{
const int size = SDL_min(len, channels[i].sound->length - channels[i].pos); const int size = SDL_min(len, channels[i].sound->length - channels[i].pos);
SDL_MixAudioFormat(stream, channels[i].sound->buffer + channels[i].pos, AUDIO_S16, size, JA_soundVolume); SDL_MixAudioFormat(stream, channels[i].sound->buffer + channels[i].pos, AUDIO_S16, size, JA_soundVolume);
channels[i].pos += size; channels[i].pos += size;
if (size < len) if (size < len) {
{ if (channels[i].times != 0) {
if (channels[i].times != 0)
{
SDL_MixAudioFormat(stream + size, channels[i].sound->buffer, AUDIO_S16, len - size, JA_soundVolume); SDL_MixAudioFormat(stream + size, channels[i].sound->buffer, AUDIO_S16, len - size, JA_soundVolume);
channels[i].pos = len - size; channels[i].pos = len - size;
if (channels[i].times > 0) if (channels[i].times > 0)
channels[i].times--; channels[i].times--;
} } else {
else
{
JA_StopChannel(i); JA_StopChannel(i);
} }
} }
@@ -117,8 +100,7 @@ void audioCallback(void *userdata, uint8_t *stream, int len)
} }
} }
void JA_Init(const int freq, const SDL_AudioFormat format, const int channels) void JA_Init(const int freq, const SDL_AudioFormat format, const int channels) {
{
JA_freq = freq; JA_freq = freq;
JA_format = format; JA_format = format;
JA_channels = channels; JA_channels = channels;
@@ -129,32 +111,29 @@ void JA_Init(const int freq, const SDL_AudioFormat format, const int channels)
SDL_PauseAudioDevice(sdlAudioDevice, 0); SDL_PauseAudioDevice(sdlAudioDevice, 0);
} }
void JA_Quit() void JA_Quit() {
{
SDL_PauseAudioDevice(sdlAudioDevice, 1); SDL_PauseAudioDevice(sdlAudioDevice, 1);
if (sdlAudioDevice != 0) if (sdlAudioDevice != 0)
SDL_CloseAudioDevice(sdlAudioDevice); SDL_CloseAudioDevice(sdlAudioDevice);
sdlAudioDevice = 0; sdlAudioDevice = 0;
} }
JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length) JA_Music_t* JA_LoadMusic(Uint8* buffer, Uint32 length) {
{
int chan, samplerate; int chan, samplerate;
JA_Music_t *music = new JA_Music_t(); JA_Music_t* music = new JA_Music_t();
music->samples = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &music->output); music->samples = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &music->output);
SDL_AudioCVT cvt; SDL_AudioCVT cvt;
SDL_BuildAudioCVT(&cvt, AUDIO_S16, chan, samplerate, JA_format, JA_channels, JA_freq); SDL_BuildAudioCVT(&cvt, AUDIO_S16, chan, samplerate, JA_format, JA_channels, JA_freq);
if (cvt.needed) if (cvt.needed) {
{
cvt.len = music->samples * chan * 2; cvt.len = music->samples * chan * 2;
music->length = cvt.len; music->length = cvt.len;
cvt.buf = (Uint8 *)SDL_malloc(cvt.len * cvt.len_mult); cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult);
SDL_memcpy(cvt.buf, music->output, cvt.len); SDL_memcpy(cvt.buf, music->output, cvt.len);
SDL_ConvertAudio(&cvt); SDL_ConvertAudio(&cvt);
free(music->output); free(music->output);
music->output = (short *)cvt.buf; music->output = (short*)cvt.buf;
} }
music->length = music->samples * chan * 2; music->length = music->samples * chan * 2;
music->pos = 0; music->pos = 0;
@@ -163,32 +142,29 @@ JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length)
return music; 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. // [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"); FILE* f = fopen(filename, "rb");
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
long fsize = ftell(f); long fsize = ftell(f);
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
Uint8 *buffer = (Uint8 *)malloc(fsize + 1); Uint8* buffer = (Uint8*)malloc(fsize + 1);
if (fread(buffer, fsize, 1, f) != 1) if (fread(buffer, fsize, 1, f) != 1)
return NULL; return NULL;
fclose(f); fclose(f);
JA_Music_t *music = JA_LoadMusic(buffer, fsize); JA_Music_t* music = JA_LoadMusic(buffer, fsize);
free(buffer); free(buffer);
return music; 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) if (!JA_musicEnabled)
return; return;
if (current_music != NULL) if (current_music != NULL) {
{
current_music->pos = 0; current_music->pos = 0;
current_music->state = JA_MUSIC_STOPPED; current_music->state = JA_MUSIC_STOPPED;
} }
@@ -198,8 +174,7 @@ void JA_PlayMusic(JA_Music_t *music, const int loop)
current_music->times = loop; current_music->times = loop;
} }
void JA_PauseMusic() void JA_PauseMusic() {
{
if (!JA_musicEnabled) if (!JA_musicEnabled)
return; return;
@@ -208,8 +183,7 @@ void JA_PauseMusic()
current_music->state = JA_MUSIC_PAUSED; current_music->state = JA_MUSIC_PAUSED;
} }
void JA_ResumeMusic() void JA_ResumeMusic() {
{
if (!JA_musicEnabled) if (!JA_musicEnabled)
return; return;
@@ -218,8 +192,7 @@ void JA_ResumeMusic()
current_music->state = JA_MUSIC_PLAYING; current_music->state = JA_MUSIC_PLAYING;
} }
void JA_StopMusic() void JA_StopMusic() {
{
if (!JA_musicEnabled) if (!JA_musicEnabled)
return; return;
@@ -229,8 +202,7 @@ void JA_StopMusic()
current_music->state = JA_MUSIC_STOPPED; current_music->state = JA_MUSIC_STOPPED;
} }
void JA_FadeOutMusic(const int milliseconds) void JA_FadeOutMusic(const int milliseconds) {
{
if (!JA_musicEnabled) if (!JA_musicEnabled)
return; return;
if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) if (current_music == NULL || current_music->state == JA_MUSIC_INVALID)
@@ -242,8 +214,7 @@ void JA_FadeOutMusic(const int milliseconds)
fade_initial_volume = JA_musicVolume; fade_initial_volume = JA_musicVolume;
} }
JA_Music_state JA_GetMusicState() JA_Music_state JA_GetMusicState() {
{
if (!JA_musicEnabled) if (!JA_musicEnabled)
return JA_MUSIC_DISABLED; return JA_MUSIC_DISABLED;
@@ -252,61 +223,54 @@ JA_Music_state JA_GetMusicState()
return current_music->state; return current_music->state;
} }
void JA_DeleteMusic(JA_Music_t *music) void JA_DeleteMusic(JA_Music_t* music) {
{
if (current_music == music) if (current_music == music)
current_music = NULL; current_music = NULL;
free(music->output); free(music->output);
delete music; delete music;
} }
int JA_SetMusicVolume(int volume) int JA_SetMusicVolume(int volume) {
{
JA_musicVolume = volume > 128 ? 128 : volume < 0 ? 0 JA_musicVolume = volume > 128 ? 128 : volume < 0 ? 0
: volume; : volume;
return JA_musicVolume; return JA_musicVolume;
} }
void JA_SetMusicPosition(float value) void JA_SetMusicPosition(float value) {
{
if (!current_music) if (!current_music)
return; return;
current_music->pos = value * JA_freq; current_music->pos = value * JA_freq;
} }
float JA_GetMusicPosition() float JA_GetMusicPosition() {
{
if (!current_music) if (!current_music)
return 0; return 0;
return float(current_music->pos) / float(JA_freq); return float(current_music->pos) / float(JA_freq);
} }
void JA_EnableMusic(const bool value) void JA_EnableMusic(const bool value) {
{
if (!value && current_music != NULL && current_music->state == JA_MUSIC_PLAYING) if (!value && current_music != NULL && current_music->state == JA_MUSIC_PLAYING)
JA_StopMusic(); JA_StopMusic();
JA_musicEnabled = value; 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();
JA_Sound_t *sound = new JA_Sound_t();
sound->buffer = buffer; sound->buffer = buffer;
sound->length = length; sound->length = length;
return sound; 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();
JA_Sound_t *sound = new JA_Sound_t();
SDL_AudioSpec wavSpec; SDL_AudioSpec wavSpec;
SDL_LoadWAV_RW(SDL_RWFromMem(buffer, size), 1, &wavSpec, &sound->buffer, &sound->length); SDL_LoadWAV_RW(SDL_RWFromMem(buffer, size), 1, &wavSpec, &sound->buffer, &sound->length);
SDL_AudioCVT cvt; SDL_AudioCVT cvt;
SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq, JA_format, JA_channels, JA_freq); SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq, JA_format, JA_channels, JA_freq);
cvt.len = sound->length; cvt.len = sound->length;
cvt.buf = (Uint8 *)SDL_malloc(cvt.len * cvt.len_mult); cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult);
SDL_memcpy(cvt.buf, sound->buffer, sound->length); SDL_memcpy(cvt.buf, sound->buffer, sound->length);
SDL_ConvertAudio(&cvt); SDL_ConvertAudio(&cvt);
SDL_FreeWAV(sound->buffer); SDL_FreeWAV(sound->buffer);
@@ -316,16 +280,15 @@ JA_Sound_t *JA_LoadSound(uint8_t *buffer, uint32_t size)
return sound; 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();
JA_Sound_t *sound = new JA_Sound_t();
SDL_AudioSpec wavSpec; SDL_AudioSpec wavSpec;
SDL_LoadWAV(filename, &wavSpec, &sound->buffer, &sound->length); SDL_LoadWAV(filename, &wavSpec, &sound->buffer, &sound->length);
SDL_AudioCVT cvt; SDL_AudioCVT cvt;
SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq, JA_format, JA_channels, JA_freq); SDL_BuildAudioCVT(&cvt, wavSpec.format, wavSpec.channels, wavSpec.freq, JA_format, JA_channels, JA_freq);
cvt.len = sound->length; cvt.len = sound->length;
cvt.buf = (Uint8 *)SDL_malloc(cvt.len * cvt.len_mult); cvt.buf = (Uint8*)SDL_malloc(cvt.len * cvt.len_mult);
SDL_memcpy(cvt.buf, sound->buffer, sound->length); SDL_memcpy(cvt.buf, sound->buffer, sound->length);
SDL_ConvertAudio(&cvt); SDL_ConvertAudio(&cvt);
SDL_FreeWAV(sound->buffer); SDL_FreeWAV(sound->buffer);
@@ -335,14 +298,12 @@ JA_Sound_t *JA_LoadSound(const char *filename)
return sound; return sound;
} }
int JA_PlaySound(JA_Sound_t *sound, const int loop) int JA_PlaySound(JA_Sound_t* sound, const int loop) {
{
if (!JA_soundEnabled) if (!JA_soundEnabled)
return -1; return -1;
int channel = 0; 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++; channel++;
} }
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) if (channel == JA_MAX_SIMULTANEOUS_CHANNELS)
@@ -355,8 +316,7 @@ int JA_PlaySound(JA_Sound_t *sound, const int loop)
return channel; return channel;
} }
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop) int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop) {
{
if (!JA_soundEnabled) if (!JA_soundEnabled)
return -1; return -1;
@@ -370,10 +330,8 @@ int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop)
return channel; return channel;
} }
void JA_DeleteSound(JA_Sound_t *sound) void JA_DeleteSound(JA_Sound_t* sound) {
{ for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
{
if (channels[i].sound == sound) if (channels[i].sound == sound)
JA_StopChannel(i); JA_StopChannel(i);
} }
@@ -381,70 +339,54 @@ void JA_DeleteSound(JA_Sound_t *sound)
delete sound; delete sound;
} }
void JA_PauseChannel(const int channel) void JA_PauseChannel(const int channel) {
{
if (!JA_soundEnabled) if (!JA_soundEnabled)
return; return;
if (channel == -1) if (channel == -1) {
{ for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
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; channels[i].state = JA_CHANNEL_PAUSED;
} }
} } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
if (channels[channel].state == JA_CHANNEL_PLAYING) if (channels[channel].state == JA_CHANNEL_PLAYING)
channels[channel].state = JA_CHANNEL_PAUSED; channels[channel].state = JA_CHANNEL_PAUSED;
} }
} }
void JA_ResumeChannel(const int channel) void JA_ResumeChannel(const int channel) {
{
if (!JA_soundEnabled) if (!JA_soundEnabled)
return; return;
if (channel == -1) if (channel == -1) {
{ for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
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; channels[i].state = JA_CHANNEL_PLAYING;
} }
} } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
if (channels[channel].state == JA_CHANNEL_PAUSED) if (channels[channel].state == JA_CHANNEL_PAUSED)
channels[channel].state = JA_CHANNEL_PLAYING; channels[channel].state = JA_CHANNEL_PLAYING;
} }
} }
void JA_StopChannel(const int channel) void JA_StopChannel(const int channel) {
{
if (!JA_soundEnabled) if (!JA_soundEnabled)
return; return;
if (channel == -1) if (channel == -1) {
{ for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
{
channels[i].state = JA_CHANNEL_FREE; channels[i].state = JA_CHANNEL_FREE;
channels[i].pos = 0; channels[i].pos = 0;
channels[i].sound = NULL; channels[i].sound = NULL;
} }
} } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
{
channels[channel].state = JA_CHANNEL_FREE; channels[channel].state = JA_CHANNEL_FREE;
channels[channel].pos = 0; channels[channel].pos = 0;
channels[channel].sound = NULL; channels[channel].sound = NULL;
} }
} }
JA_Channel_state JA_GetChannelState(const int channel) JA_Channel_state JA_GetChannelState(const int channel) {
{
if (!JA_soundEnabled) if (!JA_soundEnabled)
return JA_SOUND_DISABLED; return JA_SOUND_DISABLED;
@@ -453,25 +395,21 @@ JA_Channel_state JA_GetChannelState(const int channel)
return channels[channel].state; return channels[channel].state;
} }
int JA_SetSoundVolume(int volume) int JA_SetSoundVolume(int volume) {
{
JA_soundVolume = volume > 128 ? 128 : volume < 0 ? 0 JA_soundVolume = volume > 128 ? 128 : volume < 0 ? 0
: volume; : volume;
return JA_soundVolume; return JA_soundVolume;
} }
void JA_EnableSound(const bool value) void JA_EnableSound(const bool value) {
{ for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
{
if (channels[i].state == JA_CHANNEL_PLAYING) if (channels[i].state == JA_CHANNEL_PLAYING)
JA_StopChannel(i); JA_StopChannel(i);
} }
JA_soundEnabled = value; JA_soundEnabled = value;
} }
int JA_SetVolume(int volume) int JA_SetVolume(int volume) {
{
JA_musicVolume = volume > 128 ? 128 : volume < 0 ? 0 JA_musicVolume = volume > 128 ? 128 : volume < 0 ? 0
: volume; : volume;
JA_soundVolume = JA_musicVolume / 2; JA_soundVolume = JA_musicVolume / 2;

View File

@@ -1,20 +1,18 @@
#pragma once #pragma once
#include <SDL2/SDL_audio.h> // Para SDL_AudioFormat #include <SDL3/SDL_audio.h> // Para SDL_AudioFormat
#include <SDL2/SDL_stdinc.h> // Para Uint32, Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint32, Uint8
struct JA_Music_t; // lines 5-5 struct JA_Music_t; // lines 5-5
struct JA_Sound_t; // lines 6-6 struct JA_Sound_t; // lines 6-6
enum JA_Channel_state enum JA_Channel_state {
{
JA_CHANNEL_INVALID, JA_CHANNEL_INVALID,
JA_CHANNEL_FREE, JA_CHANNEL_FREE,
JA_CHANNEL_PLAYING, JA_CHANNEL_PLAYING,
JA_CHANNEL_PAUSED, JA_CHANNEL_PAUSED,
JA_SOUND_DISABLED JA_SOUND_DISABLED
}; };
enum JA_Music_state enum JA_Music_state {
{
JA_MUSIC_INVALID, JA_MUSIC_INVALID,
JA_MUSIC_PLAYING, JA_MUSIC_PLAYING,
JA_MUSIC_PAUSED, JA_MUSIC_PAUSED,
@@ -28,30 +26,30 @@ struct JA_Music_t;
void JA_Init(const int freq, const SDL_AudioFormat format, const int channels); void JA_Init(const int freq, const SDL_AudioFormat format, const int channels);
void JA_Quit(); void JA_Quit();
JA_Music_t *JA_LoadMusic(const char *filename); JA_Music_t* JA_LoadMusic(const char* filename);
JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length); JA_Music_t* JA_LoadMusic(Uint8* buffer, Uint32 length);
void JA_PlayMusic(JA_Music_t *music, const int loop = -1); void JA_PlayMusic(JA_Music_t* music, const int loop = -1);
void JA_PauseMusic(); void JA_PauseMusic();
void JA_ResumeMusic(); void JA_ResumeMusic();
void JA_StopMusic(); void JA_StopMusic();
void JA_FadeOutMusic(const int milliseconds); void JA_FadeOutMusic(const int milliseconds);
JA_Music_state JA_GetMusicState(); JA_Music_state JA_GetMusicState();
void JA_DeleteMusic(JA_Music_t *music); void JA_DeleteMusic(JA_Music_t* music);
int JA_SetMusicVolume(int volume); int JA_SetMusicVolume(int volume);
void JA_SetMusicPosition(float value); void JA_SetMusicPosition(float value);
float JA_GetMusicPosition(); float JA_GetMusicPosition();
void JA_EnableMusic(const bool value); void JA_EnableMusic(const bool value);
JA_Sound_t *JA_NewSound(Uint8 *buffer, Uint32 length); JA_Sound_t* JA_NewSound(Uint8* buffer, Uint32 length);
JA_Sound_t *JA_LoadSound(Uint8 *buffer, Uint32 length); JA_Sound_t* JA_LoadSound(Uint8* buffer, Uint32 length);
JA_Sound_t *JA_LoadSound(const char *filename); JA_Sound_t* JA_LoadSound(const char* filename);
int JA_PlaySound(JA_Sound_t *sound, const int loop = 0); int JA_PlaySound(JA_Sound_t* sound, const int loop = 0);
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop = 0); int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop = 0);
void JA_PauseChannel(const int channel); void JA_PauseChannel(const int channel);
void JA_ResumeChannel(const int channel); void JA_ResumeChannel(const int channel);
void JA_StopChannel(const int channel); void JA_StopChannel(const int channel);
JA_Channel_state JA_GetChannelState(const int channel); JA_Channel_state JA_GetChannelState(const int channel);
void JA_DeleteSound(JA_Sound_t *sound); void JA_DeleteSound(JA_Sound_t* sound);
int JA_SetSoundVolume(int volume); int JA_SetSoundVolume(int volume);
void JA_EnableSound(const bool value); void JA_EnableSound(const bool value);

267
source/external/jail_shader.cpp vendored Normal file
View File

@@ -0,0 +1,267 @@
#include "jail_shader.h"
#include <SDL3/SDL_rect.h> // Para SDL_Point
#include <SDL3/SDL_stdinc.h> // Para SDL_bool
#include <cstring> // Para strncmp
#include <iostream> // Para basic_ostream, operator<<, endl, cout
#include <stdexcept> // Para runtime_error
#include <vector> // Para vector
#ifdef __APPLE__
#include <OpenGL/OpenGL.h> // Para OpenGL 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, glTexCoord2f, glVertex2f, GLfloat
#endif // __APPLE__
namespace shader {
SDL_Window* win = nullptr;
SDL_Renderer* renderer = nullptr;
GLuint programId = 0;
SDL_Texture* backBuffer = nullptr;
SDL_Point win_size = {320 * 4, 256 * 4};
SDL_Point 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;
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");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram;
}
#endif
// Función para compilar un shader a partir de un std::string
GLuint compileShader(const std::string& source, GLuint shaderType) {
if (source.empty()) {
throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío.");
}
// Crear identificador del shader
GLuint resultado = glCreateShader(shaderType);
// Agregar una directiva según el tipo de shader
std::string directiva = (shaderType == GL_VERTEX_SHADER)
? "#define VERTEX\n"
: "#define FRAGMENT\n";
const char* sources[2] = {directiva.c_str(), source.c_str()};
// Especificar el código fuente del shader
glShaderSource(resultado, 2, sources, nullptr);
// Compilar el shader
glCompileShader(resultado);
// Verificar si la compilación fue exitosa
GLint compiladoCorrectamente = GL_FALSE;
glGetShaderiv(resultado, GL_COMPILE_STATUS, &compiladoCorrectamente);
if (compiladoCorrectamente != GL_TRUE) {
std::cout << "Error en la compilación del shader (" << resultado << ")!" << std::endl;
GLint longitudLog;
glGetShaderiv(resultado, GL_INFO_LOG_LENGTH, &longitudLog);
if (longitudLog > 0) {
std::vector<GLchar> log(longitudLog);
glGetShaderInfoLog(resultado, longitudLog, &longitudLog, log.data());
std::cout << "Registro de compilación del shader: " << log.data() << std::endl;
}
glDeleteShader(resultado);
resultado = 0;
}
return resultado;
}
// Función para compilar un programa de shaders (vertex y fragment) a partir de std::string
GLuint compileProgram(const std::string& vertexShaderSource, const std::string& fragmentShaderSource) {
GLuint idPrograma = glCreateProgram();
// Si el fragment shader está vacío, reutilizamos el código del vertex shader
GLuint idShaderVertice = compileShader(vertexShaderSource, GL_VERTEX_SHADER);
GLuint idShaderFragmento = compileShader(fragmentShaderSource.empty() ? vertexShaderSource : fragmentShaderSource, GL_FRAGMENT_SHADER);
if (idShaderVertice && idShaderFragmento) {
// Asociar los shaders al programa
glAttachShader(idPrograma, idShaderVertice);
glAttachShader(idPrograma, idShaderFragmento);
glLinkProgram(idPrograma);
glValidateProgram(idPrograma);
// Verificar el estado del enlace
GLint longitudLog;
glGetProgramiv(idPrograma, GL_INFO_LOG_LENGTH, &longitudLog);
if (longitudLog > 0) {
std::vector<char> log(longitudLog);
glGetProgramInfoLog(idPrograma, longitudLog, &longitudLog, log.data());
std::cout << "Registro de información del programa:" << std::endl
<< log.data() << std::endl;
}
}
if (idShaderVertice) {
glDeleteShader(idShaderVertice);
}
if (idShaderFragmento) {
glDeleteShader(idShaderFragmento);
}
return idPrograma;
}
bool init(SDL_Window* ventana, SDL_Texture* texturaBackBuffer, const std::string& vertexShader, const std::string& fragmentShader) {
shader::win = ventana;
shader::renderer = SDL_GetRenderer(ventana);
shader::backBuffer = texturaBackBuffer;
SDL_GetWindowSize(ventana, &win_size.x, &win_size.y);
int acceso;
SDL_QueryTexture(texturaBackBuffer, nullptr, &acceso, &tex_size.x, &tex_size.y);
if (acceso != SDL_TEXTUREACCESS_TARGET) {
throw std::runtime_error("ERROR FATAL: La textura debe tener definido SDL_TEXTUREACCESS_TARGET.");
}
SDL_RendererInfo infoRenderer;
SDL_GetRendererInfo(renderer, &infoRenderer);
// Verificar que el renderer sea OpenGL
if (!strncmp(infoRenderer.name, "opengl", 6)) {
#ifndef __APPLE__
if (!initGLExtensions()) {
std::cout << "ADVERTENCIA: No se han podido inicializar las extensiones de OpenGL." << std::endl;
usingOpenGL = false;
return false;
}
#endif
// Compilar el programa de shaders utilizando std::string
programId = compileProgram(vertexShader, fragmentShader);
} else {
std::cout << "ADVERTENCIA: El driver del renderer no es OpenGL." << std::endl;
usingOpenGL = false;
return false;
}
usingOpenGL = true;
return true;
}
void render() {
GLint oldProgramId;
// Establece el color de fondo
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderClear(renderer);
if (usingOpenGL) {
SDL_GL_BindTexture(backBuffer, nullptr, nullptr);
if (programId != 0) {
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
glUseProgram(programId);
}
// Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize
int logicalW, logicalH;
SDL_RenderGetLogicalSize(renderer, &logicalW, &logicalH);
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;
SDL_bool useIntegerScale = SDL_RenderGetIntegerScale(renderer);
if (useIntegerScale) {
// 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;
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;
}
}
glViewport(viewportX, viewportY, viewportW, viewportH);
// 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();
SDL_GL_SwapWindow(win);
if (programId != 0) {
glUseProgram(oldProgramId);
}
} else {
SDL_RenderCopy(renderer, backBuffer, nullptr, nullptr);
SDL_RenderPresent(renderer);
}
}
} // namespace shader

View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
#include <SDL2/SDL_render.h> // Para SDL_Texture #include <SDL3/SDL_render.h> // Para SDL_Texture
#include <SDL2/SDL_video.h> // Para SDL_Window #include <SDL3/SDL_video.h> // Para SDL_Window
#include <string> #include <string>
// TIPS: // TIPS:
@@ -37,9 +38,8 @@
// Ah! una cosa mes: al compilar, en Linux afegir "-lGL", en Windows afegir "-lopengl32". // Ah! una cosa mes: al compilar, en Linux afegir "-lGL", en Windows afegir "-lopengl32".
// En Mac ni idea // En Mac ni idea
namespace shader namespace shader {
{ // const bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const char *vertexShader, const char *fragmentShader = nullptr);
// const bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const char *vertexShader, const char *fragmentShader = nullptr); bool init(SDL_Window* ventana, SDL_Texture* texturaBackBuffer, const std::string& vertexShader, const std::string& fragmentShader = "");
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = ""); void render();
void render(); } // namespace shader
}

View File

@@ -1,709 +0,0 @@
#include "game.h"
#include <SDL2/SDL_render.h> // Para SDL_FLIP_HORIZONTAL
#include <SDL2/SDL_scancode.h> // Para SDL_SCANCODE_7, SDL_SCANCODE_A, SDL_S...
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
#include <vector> // Para vector
#include "asset.h" // Para Asset
#include "cheevos.h" // Para Cheevos
#include "debug.h" // Para Debug
#include "defines.h" // Para BLOCK, PLAY_AREA_HEIGHT, RoomBorder::BOTTOM
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, InputAction, INPUT_DO_NOT_ALLOW_REPEAT
#include "item_tracker.h" // Para ItemTracker
#include "jail_audio.h" // Para JA_PauseMusic, JA_GetMusicState, JA_P...
#include "notifier.h" // Para Notifier, NotificationText, CHEEVO_NO...
#include "options.h" // Para Options, options, Cheat, SectionState
#include "resource.h" // Para ResourceRoom, Resource
#include "room.h" // Para Room, RoomData
#include "room_tracker.h" // Para RoomTracker
#include "scoreboard.h" // Para ScoreboardData, Scoreboard
#include "screen.h" // Para Screen
#include "stats.h" // Para Stats
#include "surface.h" // Para Surface
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "utils.h" // Para PaletteColor, stringToColor
// Constructor
Game::Game(GameMode mode)
: board_(std::make_shared<ScoreboardData>(0, 9, 0, true, 0, SDL_GetTicks(), options.cheats.jail_is_open == Cheat::CheatState::ENABLED)),
scoreboard_(std::make_shared<Scoreboard>(board_)),
room_tracker_(std::make_shared<RoomTracker>()),
stats_(std::make_shared<Stats>(Asset::get()->get("stats.csv"), Asset::get()->get("stats_buffer.csv"))),
mode_(mode),
#ifdef DEBUG
current_room_("03.room"),
spawn_point_(PlayerSpawn(25 * BLOCK, 13 * BLOCK, 0, 0, 0, PlayerState::STANDING, SDL_FLIP_HORIZONTAL))
#else
current_room_("03.room"),
spawn_point_(PlayerSpawn(25 * BLOCK, 13 * BLOCK, 0, 0, 0, PlayerState::STANDING, SDL_FLIP_HORIZONTAL))
#endif
{
#ifdef DEBUG
Debug::get()->setEnabled(false);
#endif
// Crea objetos e inicializa variables
ItemTracker::init();
DEMO_init();
room_ = std::make_shared<Room>(current_room_, board_);
initPlayer(spawn_point_, room_);
initStats();
total_items_ = getTotalItems();
createRoomNameTexture();
changeRoom(current_room_);
Cheevos::get()->enable(!options.cheats.enabled()); // Deshabilita los logros si hay trucos activados
Cheevos::get()->clearUnobtainableState();
options.section.section = (mode_ == GameMode::GAME) ? Section::GAME : Section::DEMO;
options.section.subsection = Subsection::NONE;
}
Game::~Game()
{
ItemTracker::destroy();
}
// Comprueba los eventos de la cola
void Game::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
globalEvents::check(event);
#ifdef DEBUG
checkDebugEvents(event);
#endif
}
}
// Comprueba el teclado
void Game::checkInput()
{
if (Input::get()->checkInput(InputAction::TOGGLE_MUSIC, INPUT_DO_NOT_ALLOW_REPEAT))
{
board_->music = !board_->music;
board_->music ? JA_ResumeMusic() : JA_PauseMusic();
Notifier::get()->show({"MUSIC " + std::string(board_->music ? "ENABLED" : "DISABLED")}, NotificationText::CENTER);
}
else if (Input::get()->checkInput(InputAction::PAUSE, INPUT_DO_NOT_ALLOW_REPEAT))
{
togglePause();
Notifier::get()->show({std::string(paused_ ? "GAME PAUSED" : "GAME RUNNING")}, NotificationText::CENTER);
}
globalInputs::check();
}
// Bucle para el juego
void Game::run()
{
keepMusicPlaying();
if (!board_->music && mode_ == GameMode::GAME)
{
JA_PauseMusic();
}
while (options.section.section == Section::GAME || options.section.section == Section::DEMO)
{
update();
checkEvents();
render();
}
if (mode_ == GameMode::GAME)
{
JA_StopMusic();
}
}
// Actualiza el juego, las variables, comprueba la entrada, etc.
void Game::update()
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED)
{
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba el teclado
checkInput();
#ifdef DEBUG
Debug::get()->clear();
#endif
// Actualiza los objetos
room_->update();
if (mode_ == GameMode::GAME)
{
player_->update();
checkPlayerIsOnBorder();
checkPlayerAndItems();
checkPlayerAndEnemies();
checkIfPlayerIsAlive();
checkGameOver();
checkEndGame();
checkRestoringJail();
checkSomeCheevos();
}
DEMO_checkRoomChange();
scoreboard_->update();
keepMusicPlaying();
updateBlackScreen();
Screen::get()->update();
#ifdef DEBUG
updateDebugInfo();
#endif
}
}
// Pinta los objetos en pantalla
void Game::render()
{
// Prepara para dibujar el frame
Screen::get()->start();
// Dibuja los elementos del juego en orden
room_->renderMap();
room_->renderEnemies();
room_->renderItems();
if (mode_ == GameMode::GAME)
{
player_->render();
}
renderRoomName();
scoreboard_->render();
renderBlackScreen();
#ifdef DEBUG
// Debug info
renderDebugInfo();
#endif
// Actualiza la pantalla
Screen::get()->render();
}
#ifdef DEBUG
// Pasa la información de debug
void Game::updateDebugInfo()
{
Debug::get()->add("X = " + std::to_string(static_cast<int>(player_->x_)) + ", Y = " + std::to_string(static_cast<int>(player_->y_)));
Debug::get()->add("VX = " + std::to_string(player_->vx_).substr(0, 4) + ", VY = " + std::to_string(player_->vy_).substr(0, 4));
Debug::get()->add("STATE = " + std::to_string(static_cast<int>(player_->state_)));
}
// Pone la información de debug en pantalla
void Game::renderDebugInfo()
{
if (!Debug::get()->getEnabled())
{
return;
}
auto surface = Screen::get()->getRendererSurface();
// Borra el marcador
SDL_Rect rect = {0, 18 * BLOCK, PLAY_AREA_WIDTH, GAMECANVAS_HEIGHT - PLAY_AREA_HEIGHT};
surface->fillRect(&rect, static_cast<Uint8>(PaletteColor::BLACK));
// Pinta la rejilla
/*for (int i = 0; i < PLAY_AREA_BOTTOM; i += 8)
{
// Lineas horizontales
surface->drawLine(0, i, PLAY_AREA_RIGHT, i, static_cast<Uint8>(PaletteColor::BRIGHT_BLACK));
}
for (int i = 0; i < PLAY_AREA_RIGHT; i += 8)
{
// Lineas verticales
surface->drawLine(i, 0, i, PLAY_AREA_BOTTOM - 1, static_cast<Uint8>(PaletteColor::BRIGHT_BLACK));
}*/
// Pinta el texto
Debug::get()->setPos({1, 18 * 8});
Debug::get()->render();
}
// Comprueba los eventos
void Game::checkDebugEvents(const SDL_Event &event)
{
if (event.type == SDL_KEYDOWN && event.key.repeat == 0)
{
switch (event.key.keysym.scancode)
{
case SDL_SCANCODE_G:
Debug::get()->toggleEnabled();
options.cheats.invincible = static_cast<Cheat::CheatState>(Debug::get()->getEnabled());
board_->music = !Debug::get()->getEnabled();
board_->music ? JA_ResumeMusic() : JA_PauseMusic();
break;
case SDL_SCANCODE_R:
Resource::get()->reload();
break;
case SDL_SCANCODE_W:
changeRoom(room_->getRoom(RoomBorder::TOP));
break;
case SDL_SCANCODE_A:
changeRoom(room_->getRoom(RoomBorder::LEFT));
break;
case SDL_SCANCODE_S:
changeRoom(room_->getRoom(RoomBorder::BOTTOM));
break;
case SDL_SCANCODE_D:
changeRoom(room_->getRoom(RoomBorder::RIGHT));
break;
case SDL_SCANCODE_7:
Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", "I LIKE MY MULTICOLOURED FRIENDS"}, NotificationText::CENTER, CHEEVO_NOTIFICATION_DURATION, -1, false, "F7");
break;
default:
break;
}
}
}
#endif
// Escribe el nombre de la pantalla
void Game::renderRoomName()
{
// Dibuja la textura con el nombre de la habitación
room_name_surface_->render(nullptr, &room_name_rect_);
}
// Cambia de habitación
bool Game::changeRoom(const std::string &room_path)
{
// En las habitaciones los limites tienen la cadena del fichero o un 0 en caso de no limitar con nada
if (room_path == "0")
{
return false;
}
// Verifica que exista el fichero que se va a cargar
if (Asset::get()->get(room_path) != "")
{
// Crea un objeto habitación nuevo a partir del fichero
room_ = std::make_shared<Room>(room_path, board_);
// Pone el nombre de la habitación en la textura
fillRoomNameTexture();
// Pone el color del marcador en función del color del borde de la habitación
setScoreBoardColor();
if (room_tracker_->addRoom(room_path))
{
// Incrementa el contador de habitaciones visitadas
board_->rooms++;
options.stats.rooms = board_->rooms;
// Actualiza las estadisticas
stats_->addVisit(room_->getName());
}
// Pasa la nueva habitación al jugador
player_->setRoom(room_);
// Cambia la habitación actual
current_room_ = room_path;
return true;
}
return false;
}
// Comprueba si el jugador esta en el borde de la pantalla
void Game::checkPlayerIsOnBorder()
{
if (player_->getOnBorder())
{
const std::string roomName = room_->getRoom(player_->getBorder());
if (changeRoom(roomName))
{
player_->switchBorders();
spawn_point_ = player_->getSpawnParams();
}
}
}
// Comprueba las colisiones del jugador con los enemigos
bool Game::checkPlayerAndEnemies()
{
const bool death = room_->enemyCollision(player_->getCollider());
if (death)
{
killPlayer();
}
return death;
}
// Comprueba las colisiones del jugador con los objetos
void Game::checkPlayerAndItems()
{
room_->itemCollision(player_->getCollider());
}
// Comprueba si el jugador esta vivo
void Game::checkIfPlayerIsAlive()
{
if (!player_->isAlive())
{
killPlayer();
}
}
// Comprueba si ha terminado la partida
void Game::checkGameOver()
{
if (board_->lives < 0 && black_screen_counter_ > 17)
{
options.section.section = Section::GAME_OVER;
}
}
// Mata al jugador
void Game::killPlayer()
{
if (options.cheats.invincible == Cheat::CheatState::ENABLED)
{
return;
}
// Resta una vida al jugador
if (options.cheats.infinite_lives == Cheat::CheatState::DISABLED)
{
--board_->lives;
}
// Actualiza las estadisticas
stats_->addDeath(room_->getName());
// Invalida el logro de pasarse el juego sin morir
Cheevos::get()->setUnobtainable(11);
// Sonido
JA_PlaySound(Resource::get()->getSound("death.wav"));
// Pone la pantalla en negro un tiempo
setBlackScreen();
// Crea la nueva habitación y el nuevo jugador
room_ = std::make_shared<Room>(current_room_, board_);
initPlayer(spawn_point_, room_);
// Pone los objetos en pausa mientras esta la habitación en negro
room_->setPaused(true);
player_->setPaused(true);
}
// Establece la pantalla en negro
void Game::setBlackScreen()
{
black_screen_ = true;
}
// Actualiza las variables relativas a la pantalla en negro
void Game::updateBlackScreen()
{
if (black_screen_)
{
black_screen_counter_++;
if (black_screen_counter_ > 20)
{
black_screen_ = false;
black_screen_counter_ = 0;
player_->setPaused(false);
room_->setPaused(false);
Screen::get()->setBorderColor(room_->getBorderColor());
}
}
}
// Dibuja la pantalla negra
void Game::renderBlackScreen()
{
if (black_screen_)
{
auto const color = static_cast<Uint8>(PaletteColor::BLACK);
Screen::get()->setRendererSurface();
Screen::get()->clearSurface(color);
Screen::get()->setBorderColor(color);
}
}
// Pone el color del marcador en función del color del borde de la habitación
void Game::setScoreBoardColor()
{
// Obtiene el color del borde
const Uint8 BORDER_COLOR = room_->getBorderColor();
const bool IS_BLACK = BORDER_COLOR == static_cast<Uint8>(PaletteColor::BLACK);
const bool IS_BRIGHT_BLACK = BORDER_COLOR == static_cast<Uint8>(PaletteColor::BRIGHT_BLACK);
// Si el color del borde es negro o negro brillante cambia el texto del marcador a blanco
board_->color = IS_BLACK || IS_BRIGHT_BLACK ? static_cast<Uint8>(PaletteColor::WHITE) : BORDER_COLOR;
}
// Comprueba si ha finalizado el juego
bool Game::checkEndGame()
{
const bool isOnTheRoom = room_->getName() == "THE JAIL"; // Estar en la habitación que toca
const bool haveTheItems = board_->items >= int(total_items_ * 0.9f) || options.cheats.jail_is_open == Cheat::CheatState::ENABLED; // Con mas del 90% de los items recogidos
const bool isOnTheDoor = player_->getRect().x <= 128; // Y en la ubicación que toca (En la puerta)
if (haveTheItems)
{
board_->jail_is_open = true;
}
if (haveTheItems && isOnTheRoom && isOnTheDoor)
{
// Comprueba los logros de completar el juego
checkEndGameCheevos();
options.section.section = Section::ENDING;
return true;
}
return false;
}
// Obtiene la cantidad total de items que hay en el mapeado del juego
int Game::getTotalItems()
{
int items = 0;
auto rooms = Resource::get()->getRooms();
for (const auto &room : rooms)
{
items += room.room->items.size();
}
return items;
}
// Pone el juego en pausa
void Game::togglePause()
{
paused_ = !paused_;
player_->setPaused(paused_);
room_->setPaused(paused_);
scoreboard_->setPaused(paused_);
}
// Da vidas al jugador cuando está en la Jail
void Game::checkRestoringJail()
{
if (room_->getName() != "THE JAIL" || board_->lives == 9)
{
return;
}
static int counter = 0;
if (!paused_)
{
counter++;
}
// Incrementa el numero de vidas
if (counter == 100)
{
counter = 0;
board_->lives++;
JA_PlaySound(Resource::get()->getSound("death.wav"));
// Invalida el logro de completar el juego sin entrar a la jail
const bool haveTheItems = board_->items >= int(total_items_ * 0.9f);
if (!haveTheItems)
{
Cheevos::get()->setUnobtainable(9);
}
}
}
// Inicializa el diccionario de las estadísticas
void Game::initStats()
{
auto rooms = Resource::get()->getRooms();
for (const auto &room : rooms)
{
stats_->addDictionary(room.room->number, room.room->name);
}
stats_->init();
}
// Crea la textura con el nombre de la habitación
void Game::fillRoomNameTexture()
{
// Pone la textura como destino de renderizado
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(room_name_surface_);
// Rellena la textura de color
room_name_surface_->clear(stringToColor("white"));
// Escribe el texto en la textura
auto text = Resource::get()->getText("smb2");
text->writeDX(TEXT_CENTER | TEXT_COLOR, GAMECANVAS_CENTER_X, text->getCharacterSize() / 2, room_->getName(), 1, room_->getBGColor());
// Deja el renderizador por defecto
Screen::get()->setRendererSurface(previuos_renderer);
}
// Comprueba algunos logros
void Game::checkSomeCheevos()
{
auto cheevos = Cheevos::get();
// Logros sobre la cantidad de items
if (board_->items == total_items_)
{
cheevos->unlock(4);
cheevos->unlock(3);
cheevos->unlock(2);
cheevos->unlock(1);
}
else if (board_->items >= total_items_ * 0.75f)
{
cheevos->unlock(3);
cheevos->unlock(2);
cheevos->unlock(1);
}
else if (board_->items >= total_items_ * 0.5f)
{
cheevos->unlock(2);
cheevos->unlock(1);
}
else if (board_->items >= total_items_ * 0.25f)
{
cheevos->unlock(1);
}
// Logros sobre las habitaciones visitadas
if (board_->rooms >= 60)
{
cheevos->unlock(7);
cheevos->unlock(6);
cheevos->unlock(5);
}
else if (board_->rooms >= 40)
{
cheevos->unlock(6);
cheevos->unlock(5);
}
else if (board_->rooms >= 20)
{
cheevos->unlock(5);
}
}
// Comprueba los logros de completar el juego
void Game::checkEndGameCheevos()
{
auto cheevos = Cheevos::get();
// "Complete the game"
cheevos->unlock(8);
// "Complete the game without entering the jail"
cheevos->unlock(9);
// "Complete the game with all items"
if (board_->items == total_items_)
{
cheevos->unlock(10);
}
// "Complete the game without dying"
cheevos->unlock(11);
// "Complete the game in under 30 minutes"
if (scoreboard_->getMinutes() < 30)
{
cheevos->unlock(12);
}
}
// Inicializa al jugador
void Game::initPlayer(const PlayerSpawn &spawn_point, std::shared_ptr<Room> room)
{
std::string player_texture = options.cheats.alternate_skin == Cheat::CheatState::ENABLED ? "player2.gif" : "player.gif";
std::string player_animations = options.cheats.alternate_skin == Cheat::CheatState::ENABLED ? "player2.ani" : "player.ani";
const PlayerData player(spawn_point, player_texture, player_animations, room);
player_ = std::make_shared<Player>(player);
}
// Crea la textura para poner el nombre de la habitación
void Game::createRoomNameTexture()
{
auto text = Resource::get()->getText("smb2");
room_name_surface_ = std::make_shared<Surface>(options.game.width, text->getCharacterSize() * 2);
// Establece el destino de la textura
room_name_rect_ = {0, PLAY_AREA_HEIGHT, options.game.width, text->getCharacterSize() * 2};
}
// Hace sonar la música
void Game::keepMusicPlaying()
{
const std::string music_path = mode_ == GameMode::GAME ? "game.ogg" : "title.ogg";
// Si la música no está sonando
if (JA_GetMusicState() == JA_MUSIC_INVALID || JA_GetMusicState() == JA_MUSIC_STOPPED)
{
JA_PlayMusic(Resource::get()->getMusic(music_path));
}
}
// DEMO MODE: Inicializa las variables para el modo demo
void Game::DEMO_init()
{
if (mode_ == GameMode::DEMO)
{
demo_ = DemoData(0, 400, 0, {"04.room", "54.room", "20.room", "09.room", "05.room", "11.room", "31.room", "44.room"});
current_room_ = demo_.rooms.front();
}
}
// DEMO MODE: Comprueba si se ha de cambiar de habitación
void Game::DEMO_checkRoomChange()
{
if (mode_ == GameMode::DEMO)
{
demo_.counter++;
if (demo_.counter == demo_.room_time)
{
demo_.counter = 0;
demo_.room_index++;
if (demo_.room_index == (int)demo_.rooms.size())
{
options.section.section = Section::LOGO;
options.section.subsection = Subsection::LOGO_TO_TITLE;
}
else
{
changeRoom(demo_.rooms[demo_.room_index]);
}
}
}
}

View File

@@ -1,172 +0,0 @@
#pragma once
#include <SDL2/SDL_events.h> // Para SDL_Event
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_stdinc.h> // Para Uint32
#include <initializer_list> // Para initializer_list
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "player.h" // Para PlayerSpawn
class Room; // lines 12-12
class RoomTracker; // lines 13-13
class Scoreboard; // lines 14-14
class Stats; // lines 15-15
class Surface;
struct ScoreboardData; // lines 16-16
enum class GameMode
{
DEMO,
GAME
};
class Game
{
private:
// Estructuras
struct DemoData
{
int counter; // Contador para el modo demo
int room_time; // Tiempo que se muestra cada habitación
int room_index; // Índice para el vector de habitaciones
std::vector<std::string> rooms; // Listado con los mapas de la demo
// Constructor por defecto
DemoData()
: counter(0), room_time(0), room_index(0), rooms({}) {}
// Constructor parametrizado
DemoData(int counter, int room_time, int room_index, const std::vector<std::string> &rooms)
: counter(counter), room_time(room_time), room_index(room_index), rooms(rooms) {}
};
// Objetos y punteros
std::shared_ptr<ScoreboardData> board_; // Estructura con los datos del marcador
std::shared_ptr<Scoreboard> scoreboard_; // Objeto encargado de gestionar el marcador
std::shared_ptr<RoomTracker> room_tracker_; // Lleva el control de las habitaciones visitadas
std::shared_ptr<Room> room_; // Objeto encargado de gestionar cada habitación del juego
std::shared_ptr<Player> player_; // Objeto con el jugador
std::shared_ptr<Stats> stats_; // Objeto encargado de gestionar las estadísticas
std::shared_ptr<Surface> room_name_surface_; // Textura para escribir el nombre de la habitación
// Variables
GameMode mode_; // Modo del juego
DemoData demo_; // Variables para el modo demo
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::string current_room_; // Fichero de la habitación actual
PlayerSpawn spawn_point_; // Lugar de la habitación donde aparece el jugador
bool paused_ = false; // Indica si el juego se encuentra en pausa
bool black_screen_ = false; // Indica si la pantalla está en negro. Se utiliza para la muerte del jugador
int black_screen_counter_ = 0; // Contador para temporizar la pantalla en negro
int total_items_; // Cantidad total de items que hay en el mapeado del juego
SDL_Rect room_name_rect_; // Rectangulo donde pintar la textura con el nombre de la habitación
// Actualiza el juego, las variables, comprueba la entrada, etc.
void update();
// Pinta los objetos en pantalla
void render();
// Comprueba los eventos de la cola
void checkEvents();
#ifdef DEBUG
// Pone la información de debug en pantalla
void updateDebugInfo();
// Pone la información de debug en pantalla
void renderDebugInfo();
// Comprueba los eventos
void checkDebugEvents(const SDL_Event &event);
#endif
// Escribe el nombre de la pantalla
void renderRoomName();
// Cambia de habitación
bool changeRoom(const std::string &file);
// Comprueba el teclado
void checkInput();
// Comprueba si el jugador esta en el borde de la pantalla y actua
void checkPlayerIsOnBorder();
// Comprueba las colisiones del jugador con los enemigos
bool checkPlayerAndEnemies();
// Comprueba las colisiones del jugador con los objetos
void checkPlayerAndItems();
// Comprueba si el jugador esta vivo
void checkIfPlayerIsAlive();
// Comprueba si ha terminado la partida
void checkGameOver();
// Mata al jugador
void killPlayer();
// Establece la pantalla en negro
void setBlackScreen();
// Actualiza las variables relativas a la pantalla en negro
void updateBlackScreen();
// Dibuja la pantalla negra
void renderBlackScreen();
// Pone el color del marcador en función del color del borde de la habitación
void setScoreBoardColor();
// Comprueba si ha finalizado el juego
bool checkEndGame();
// Obtiene la cantidad total de items que hay en el mapeado del juego
int getTotalItems();
// Pone el juego en pausa
void togglePause();
// Da vidas al jugador cuando está en la Jail
void checkRestoringJail();
// Inicializa el diccionario de las estadísticas
void initStats();
// Pone el nombre de la habitación en la textura
void fillRoomNameTexture();
// Comprueba algunos logros
void checkSomeCheevos();
// Comprueba los logros de completar el juego
void checkEndGameCheevos();
// Inicializa al jugador
void initPlayer(const PlayerSpawn &spawn_point, std::shared_ptr<Room> room);
// Crea la textura para poner el nombre de la habitación
void createRoomNameTexture();
// Hace sonar la música
void keepMusicPlaying();
// DEMO MODE: Inicializa las variables para el modo demo
void DEMO_init();
// DEMO MODE: Comprueba si se ha de cambiar de habitación
void DEMO_checkRoomChange();
public:
// Constructor
explicit Game(GameMode mode);
// Destructor
~Game();
// Bucle para el juego
void run();
};

View File

@@ -1,57 +0,0 @@
#pragma once
#include <SDL2/SDL_stdinc.h> // Para Uint8, Uint32
#include <memory> // Para shared_ptr
#include <vector> // Para vector
class SAnimatedSprite; // lines 7-7
class GameOver
{
private:
// Constantes
static constexpr int COUNTER_SECTION_END_ = 400; // Contador: cuando acaba la sección
static constexpr int COUNTER_INIT_FADE_ = 310; // Contador: cuando emiepza el fade
static constexpr int COUNTER_FADE_LENGHT_ = 20; // Contador: duración del fade
// Objetos y punteros
std::shared_ptr<SAnimatedSprite> player_sprite_; // Sprite con el jugador
std::shared_ptr<SAnimatedSprite> tv_sprite_; // Sprite con el televisor
// Variables
int pre_counter_ = 0; // Contador previo
int counter_ = 0; // Contador
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<Uint8> colors_; // Vector con los colores para el fade
Uint8 color_; // Color usado para el texto y los sprites
// Actualiza el objeto
void update();
// Dibuja el final en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Actualiza el color usado para renderizar los textos e imagenes
void updateColor();
// Dibuja los sprites
void renderSprites();
// Actualiza los contadores
void updateCounters();
public:
// Constructor
GameOver();
// Destructor
~GameOver() = default;
// Bucle principal
void run();
};

View File

@@ -1,9 +1,8 @@
#pragma once #pragma once
#include <SDL2/SDL_events.h> #include <SDL3/SDL_events.h>
namespace globalEvents namespace globalEvents {
{ // Comprueba los eventos que se pueden producir en cualquier sección del juego
// Comprueba los eventos que se pueden producir en cualquier sección del juego void check(const SDL_Event& event);
void check(const SDL_Event &event); } // namespace globalEvents
}

View File

@@ -1,37 +1,33 @@
#include "global_inputs.h" #include "global_inputs.h"
#include <SDL2/SDL_render.h> // Para SDL_RenderSetIntegerScale
#include <SDL2/SDL_stdinc.h> // Para SDL_FALSE, SDL_TRUE #include <SDL3/SDL_render.h> // Para SDL_RenderSetIntegerScale
#include <SDL3/SDL_stdinc.h> // Para SDL_FALSE, SDL_TRUE
#include <string> // Para allocator, operator+, char_traits, string #include <string> // Para allocator, operator+, char_traits, string
#include <vector> // Para vector #include <vector> // Para vector
#include "input.h" // Para Input, InputAction, INPUT_DO_NOT_ALLOW_REPEAT #include "input.h" // Para Input, InputAction, INPUT_DO_NOT_ALLOW_REPEAT
#include "notifier.h" // Para Notifier, NotificationText #include "notifier.h" // Para Notifier, NotificationText
#include "options.h" // Para Options, options, OptionsVideo, Section #include "options.h" // Para Options, options, OptionsVideo, Section
#include "screen.h" // Para Screen #include "screen.h" // Para Screen
#include "utils.h" // Para stringInVector #include "utils.h" // Para stringInVector
namespace globalInputs namespace globalInputs {
{ void quit() {
void quit()
{
const std::string code = options.section.section == Section::GAME ? "PRESS AGAIN TO RETURN TO MENU" : "PRESS AGAIN TO EXIT"; const std::string code = options.section.section == Section::GAME ? "PRESS AGAIN TO RETURN TO MENU" : "PRESS AGAIN TO EXIT";
auto code_found = stringInVector(Notifier::get()->getCodes(), code); auto code_found = stringInVector(Notifier::get()->getCodes(), code);
if (code_found) if (code_found) {
{
// Si la notificación de salir está activa, cambia de sección // Si la notificación de salir está activa, cambia de sección
options.section.section = options.section.section == Section::GAME ? Section::TITLE : Section::QUIT; options.section.section = options.section.section == Section::GAME ? Section::TITLE : Section::QUIT;
} } else {
else
{
// Si la notificación de salir no está activa, muestra la notificación // Si la notificación de salir no está activa, muestra la notificación
Notifier::get()->show({code}, NotificationText::CENTER, 2000, -1, true, code); Notifier::get()->show({code}, NotificationText::CENTER, 2000, -1, true, code);
} }
} }
// Cambia de seccion // Cambia de seccion
void skip_section() void skip_section() {
{ switch (options.section.section) {
switch (options.section.section)
{
case Section::LOGO: case Section::LOGO:
case Section::LOADING_SCREEN: case Section::LOADING_SCREEN:
case Section::CREDITS: case Section::CREDITS:
@@ -46,78 +42,64 @@ namespace globalInputs
default: default:
break; break;
} }
} }
// Comprueba los inputs que se pueden introducir en cualquier sección del juego // Comprueba los inputs que se pueden introducir en cualquier sección del juego
void check() void check() {
{ if (Input::get()->checkInput(InputAction::EXIT, INPUT_DO_NOT_ALLOW_REPEAT)) {
if (Input::get()->checkInput(InputAction::EXIT, INPUT_DO_NOT_ALLOW_REPEAT))
{
quit(); quit();
} }
else if (Input::get()->checkInput(InputAction::ACCEPT, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::ACCEPT, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
skip_section(); skip_section();
} }
else if (Input::get()->checkInput(InputAction::TOGGLE_BORDER, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::TOGGLE_BORDER, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
Screen::get()->toggleBorder(); Screen::get()->toggleBorder();
Notifier::get()->show({"BORDER " + std::string(options.video.border.enabled ? "ENABLED" : "DISABLED")}, NotificationText::CENTER); Notifier::get()->show({"BORDER " + std::string(options.video.border.enabled ? "ENABLED" : "DISABLED")}, NotificationText::CENTER);
} }
else if (Input::get()->checkInput(InputAction::TOGGLE_VIDEOMODE, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::TOGGLE_VIDEOMODE, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
Screen::get()->toggleVideoMode(); Screen::get()->toggleVideoMode();
Notifier::get()->show({"FULLSCREEN " + std::string(options.video.mode == 0 ? "DISABLED" : "ENABLED")}, NotificationText::CENTER); Notifier::get()->show({"FULLSCREEN " + std::string(options.video.mode == 0 ? "DISABLED" : "ENABLED")}, NotificationText::CENTER);
} }
else if (Input::get()->checkInput(InputAction::WINDOW_DEC_ZOOM, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::WINDOW_DEC_ZOOM, INPUT_DO_NOT_ALLOW_REPEAT)) {
{ if (Screen::get()->decWindowZoom()) {
if (Screen::get()->decWindowZoom())
{
Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(options.window.zoom)}, NotificationText::CENTER); Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(options.window.zoom)}, NotificationText::CENTER);
} }
} }
else if (Input::get()->checkInput(InputAction::WINDOW_INC_ZOOM, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::WINDOW_INC_ZOOM, INPUT_DO_NOT_ALLOW_REPEAT)) {
{ if (Screen::get()->incWindowZoom()) {
if (Screen::get()->incWindowZoom())
{
Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(options.window.zoom)}, NotificationText::CENTER); Notifier::get()->show({"WINDOW ZOOM x" + std::to_string(options.window.zoom)}, NotificationText::CENTER);
} }
} }
else if (Input::get()->checkInput(InputAction::TOGGLE_SHADERS, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::TOGGLE_SHADERS, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
Screen::get()->toggleShaders(); Screen::get()->toggleShaders();
Notifier::get()->show({"SHADERS " + std::string(options.video.shaders ? "ENABLED" : "DISABLED")}, NotificationText::CENTER); Notifier::get()->show({"SHADERS " + std::string(options.video.shaders ? "ENABLED" : "DISABLED")}, NotificationText::CENTER);
} }
else if (Input::get()->checkInput(InputAction::NEXT_PALETTE, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::NEXT_PALETTE, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
Screen::get()->nextPalette(); Screen::get()->nextPalette();
Notifier::get()->show({"PALETTE " + options.video.palette}, NotificationText::CENTER); Notifier::get()->show({"PALETTE " + options.video.palette}, NotificationText::CENTER);
} }
else if (Input::get()->checkInput(InputAction::PREVIOUS_PALETTE, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::PREVIOUS_PALETTE, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
Screen::get()->previousPalette(); Screen::get()->previousPalette();
Notifier::get()->show({"PALETTE " + options.video.palette}, NotificationText::CENTER); Notifier::get()->show({"PALETTE " + options.video.palette}, NotificationText::CENTER);
} }
else if (Input::get()->checkInput(InputAction::TOGGLE_INTEGER_SCALE, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::TOGGLE_INTEGER_SCALE, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
options.video.integer_scale = !options.video.integer_scale; options.video.integer_scale = !options.video.integer_scale;
SDL_RenderSetIntegerScale(Screen::get()->getRenderer(), options.video.integer_scale ? SDL_TRUE : SDL_FALSE); SDL_RenderSetIntegerScale(Screen::get()->getRenderer(), options.video.integer_scale ? SDL_TRUE : SDL_FALSE);
Screen::get()->setVideoMode(options.video.mode); Screen::get()->setVideoMode(options.video.mode);
Notifier::get()->show({"INTEGER SCALE " + std::string(options.video.integer_scale ? "ENABLED" : "DISABLED")}, NotificationText::CENTER); Notifier::get()->show({"INTEGER SCALE " + std::string(options.video.integer_scale ? "ENABLED" : "DISABLED")}, NotificationText::CENTER);
} }
else if (Input::get()->checkInput(InputAction::SHOW_DEBUG_INFO, INPUT_DO_NOT_ALLOW_REPEAT)) else if (Input::get()->checkInput(InputAction::SHOW_DEBUG_INFO, INPUT_DO_NOT_ALLOW_REPEAT)) {
{
Screen::get()->toggleDebugInfo(); Screen::get()->toggleDebugInfo();
} }
}
} }
} // namespace globalInputs

View File

@@ -1,8 +1,10 @@
#include "input.h" #include "input.h"
#include <SDL2/SDL.h> // Para SDL_INIT_GAMECONTROLLER, SDL_InitSubS...
#include <SDL2/SDL_error.h> // Para SDL_GetError #include <SDL3/SDL.h> // Para SDL_INIT_GAMECONTROLLER, SDL_InitSubS...
#include <SDL2/SDL_events.h> // Para SDL_ENABLE #include <SDL3/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_keyboard.h> // Para SDL_GetKeyboardState #include <SDL3/SDL_events.h> // Para SDL_ENABLE
#include <SDL3/SDL_keyboard.h> // Para SDL_GetKeyboardState
#include <algorithm> // Para find #include <algorithm> // Para find
#include <iostream> // Para basic_ostream, operator<<, cout, endl #include <iostream> // Para basic_ostream, operator<<, cout, endl
#include <iterator> // Para distance #include <iterator> // Para distance
@@ -10,30 +12,26 @@
#include <utility> // Para pair #include <utility> // Para pair
// [SINGLETON] // [SINGLETON]
Input *Input::input_ = nullptr; Input* Input::input_ = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
void Input::init(const std::string &game_controller_db_path) void Input::init(const std::string& game_controller_db_path) {
{
Input::input_ = new Input(game_controller_db_path); Input::input_ = new Input(game_controller_db_path);
} }
// [SINGLETON] Destruiremos el objeto con esta función estática // [SINGLETON] Destruiremos el objeto con esta función estática
void Input::destroy() void Input::destroy() {
{
delete Input::input_; delete Input::input_;
} }
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
Input *Input::get() Input* Input::get() {
{
return Input::input_; return Input::input_;
} }
// Constructor // Constructor
Input::Input(const std::string &game_controller_db_path) Input::Input(const std::string& game_controller_db_path)
: game_controller_db_path_(game_controller_db_path) : game_controller_db_path_(game_controller_db_path) {
{
// Busca si hay mandos conectados // Busca si hay mandos conectados
discoverGameControllers(); discoverGameControllers();
@@ -46,62 +44,45 @@ Input::Input(const std::string &game_controller_db_path)
} }
// Asigna inputs a teclas // Asigna inputs a teclas
void Input::bindKey(InputAction input, SDL_Scancode code) void Input::bindKey(InputAction input, SDL_Scancode code) {
{
key_bindings_.at(static_cast<int>(input)).scancode = code; key_bindings_.at(static_cast<int>(input)).scancode = code;
} }
// Asigna inputs a botones del mando // Asigna inputs a botones del mando
void Input::bindGameControllerButton(int controller_index, InputAction input, SDL_GameControllerButton button) void Input::bindGameControllerButton(int controller_index, InputAction input, SDL_GameControllerButton button) {
{ if (controller_index < num_gamepads_) {
if (controller_index < num_gamepads_)
{
controller_bindings_.at(controller_index).at(static_cast<int>(input)).button = button; controller_bindings_.at(controller_index).at(static_cast<int>(input)).button = button;
} }
} }
// Asigna inputs a botones del mando // Asigna inputs a botones del mando
void Input::bindGameControllerButton(int controller_index, InputAction input_target, InputAction input_source) void Input::bindGameControllerButton(int controller_index, InputAction input_target, InputAction input_source) {
{ if (controller_index < num_gamepads_) {
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; 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 // Comprueba si un input esta activo
bool Input::checkInput(InputAction input, bool repeat, InputDeviceToUse device, int controller_index) bool Input::checkInput(InputAction input, bool repeat, InputDeviceToUse device, int controller_index) {
{
bool success_keyboard = false; bool success_keyboard = false;
bool success_controller = false; bool success_controller = false;
const int input_index = static_cast<int>(input); const int input_index = static_cast<int>(input);
if (device == InputDeviceToUse::KEYBOARD || device == InputDeviceToUse::ANY) if (device == InputDeviceToUse::KEYBOARD || device == InputDeviceToUse::ANY) {
{ const Uint8* keyStates = SDL_GetKeyboardState(nullptr);
const Uint8 *keyStates = SDL_GetKeyboardState(nullptr);
if (repeat) if (repeat) {
{
success_keyboard = keyStates[key_bindings_[input_index].scancode] != 0; success_keyboard = keyStates[key_bindings_[input_index].scancode] != 0;
} } else {
else if (!key_bindings_[input_index].active) {
{ if (keyStates[key_bindings_[input_index].scancode] != 0) {
if (!key_bindings_[input_index].active)
{
if (keyStates[key_bindings_[input_index].scancode] != 0)
{
key_bindings_[input_index].active = true; key_bindings_[input_index].active = true;
success_keyboard = true; success_keyboard = true;
} } else {
else
{
success_keyboard = false; success_keyboard = false;
} }
} } else {
else if (keyStates[key_bindings_[input_index].scancode] == 0) {
{
if (keyStates[key_bindings_[input_index].scancode] == 0)
{
key_bindings_[input_index].active = false; key_bindings_[input_index].active = false;
} }
success_keyboard = false; success_keyboard = false;
@@ -109,36 +90,23 @@ bool Input::checkInput(InputAction input, bool repeat, InputDeviceToUse device,
} }
} }
if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) {
{ if ((device == InputDeviceToUse::CONTROLLER) || (device == InputDeviceToUse::ANY)) {
if ((device == InputDeviceToUse::CONTROLLER) || (device == InputDeviceToUse::ANY))
{
success_controller = checkAxisInput(input, controller_index, repeat); success_controller = checkAxisInput(input, controller_index, repeat);
if (!success_controller) if (!success_controller) {
{ if (repeat) {
if (repeat)
{
success_controller = SDL_GameControllerGetButton(connected_controllers_.at(controller_index), controller_bindings_.at(controller_index).at(input_index).button) != 0; success_controller = SDL_GameControllerGetButton(connected_controllers_.at(controller_index), controller_bindings_.at(controller_index).at(input_index).button) != 0;
} } else {
else if (!controller_bindings_.at(controller_index).at(input_index).active) {
{ if (SDL_GameControllerGetButton(connected_controllers_.at(controller_index), controller_bindings_.at(controller_index).at(input_index).button) != 0) {
if (!controller_bindings_.at(controller_index).at(input_index).active)
{
if (SDL_GameControllerGetButton(connected_controllers_.at(controller_index), controller_bindings_.at(controller_index).at(input_index).button) != 0)
{
controller_bindings_.at(controller_index).at(input_index).active = true; controller_bindings_.at(controller_index).at(input_index).active = true;
success_controller = true; success_controller = true;
} } else {
else
{
success_controller = false; success_controller = false;
} }
} } else {
else if (SDL_GameControllerGetButton(connected_controllers_.at(controller_index), controller_bindings_.at(controller_index).at(input_index).button) == 0) {
{
if (SDL_GameControllerGetButton(connected_controllers_.at(controller_index), controller_bindings_.at(controller_index).at(input_index).button) == 0)
{
controller_bindings_.at(controller_index).at(input_index).active = false; controller_bindings_.at(controller_index).at(input_index).active = false;
} }
success_controller = false; success_controller = false;
@@ -152,30 +120,22 @@ bool Input::checkInput(InputAction input, bool repeat, InputDeviceToUse device,
} }
// Comprueba si hay almenos un input activo // Comprueba si hay almenos un input activo
bool Input::checkAnyInput(InputDeviceToUse device, int controller_index) bool Input::checkAnyInput(InputDeviceToUse device, int controller_index) {
{ if (device == InputDeviceToUse::KEYBOARD || device == InputDeviceToUse::ANY) {
if (device == InputDeviceToUse::KEYBOARD || device == InputDeviceToUse::ANY) const Uint8* mKeystates = SDL_GetKeyboardState(nullptr);
{
const Uint8 *mKeystates = SDL_GetKeyboardState(nullptr);
for (int i = 0; i < (int)key_bindings_.size(); ++i) for (int i = 0; i < (int)key_bindings_.size(); ++i) {
{ if (mKeystates[key_bindings_[i].scancode] != 0 && !key_bindings_[i].active) {
if (mKeystates[key_bindings_[i].scancode] != 0 && !key_bindings_[i].active)
{
key_bindings_[i].active = true; key_bindings_[i].active = true;
return true; return true;
} }
} }
} }
if (gameControllerFound()) if (gameControllerFound()) {
{ if (device == InputDeviceToUse::CONTROLLER || device == InputDeviceToUse::ANY) {
if (device == InputDeviceToUse::CONTROLLER || device == InputDeviceToUse::ANY) for (int i = 0; i < (int)controller_bindings_.size(); ++i) {
{ if (SDL_GameControllerGetButton(connected_controllers_[controller_index], controller_bindings_[controller_index][i].button) != 0 && !controller_bindings_[controller_index][i].active) {
for (int i = 0; i < (int)controller_bindings_.size(); ++i)
{
if (SDL_GameControllerGetButton(connected_controllers_[controller_index], controller_bindings_[controller_index][i].button) != 0 && !controller_bindings_[controller_index][i].active)
{
controller_bindings_[controller_index][i].active = true; controller_bindings_[controller_index][i].active = true;
return true; return true;
} }
@@ -187,17 +147,14 @@ bool Input::checkAnyInput(InputDeviceToUse device, int controller_index)
} }
// Busca si hay mandos conectados // Busca si hay mandos conectados
bool Input::discoverGameControllers() bool Input::discoverGameControllers() {
{
bool found = false; bool found = false;
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) != 1) if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) != 1) {
{
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
} }
if (SDL_GameControllerAddMappingsFromFile(game_controller_db_path_.c_str()) < 0) if (SDL_GameControllerAddMappingsFromFile(game_controller_db_path_.c_str()) < 0) {
{
std::cout << "Error, could not load " << game_controller_db_path_.c_str() << " file: " << SDL_GetError() << std::endl; std::cout << "Error, could not load " << game_controller_db_path_.c_str() << " file: " << SDL_GetError() << std::endl;
} }
@@ -206,44 +163,34 @@ bool Input::discoverGameControllers()
// Cuenta el número de mandos // Cuenta el número de mandos
joysticks_.clear(); joysticks_.clear();
for (int i = 0; i < num_joysticks_; ++i) for (int i = 0; i < num_joysticks_; ++i) {
{
auto joy = SDL_JoystickOpen(i); auto joy = SDL_JoystickOpen(i);
joysticks_.push_back(joy); joysticks_.push_back(joy);
if (SDL_IsGameController(i)) if (SDL_IsGameController(i)) {
{
num_gamepads_++; num_gamepads_++;
} }
} }
std::cout << "\n** LOOKING FOR GAME CONTROLLERS" << std::endl; std::cout << "\n** LOOKING FOR GAME CONTROLLERS" << std::endl;
if (num_joysticks_ != num_gamepads_) if (num_joysticks_ != num_gamepads_) {
{
std::cout << "Joysticks found: " << num_joysticks_ << std::endl; std::cout << "Joysticks found: " << num_joysticks_ << std::endl;
std::cout << "Gamepads found : " << num_gamepads_ << std::endl; std::cout << "Gamepads found : " << num_gamepads_ << std::endl;
} } else {
else
{
std::cout << "Gamepads found: " << num_gamepads_ << std::endl; std::cout << "Gamepads found: " << num_gamepads_ << std::endl;
} }
if (num_gamepads_ > 0) if (num_gamepads_ > 0) {
{
found = true; found = true;
for (int i = 0; i < num_gamepads_; i++) for (int i = 0; i < num_gamepads_; i++) {
{
// Abre el mando y lo añade a la lista // Abre el mando y lo añade a la lista
auto pad = SDL_GameControllerOpen(i); auto pad = SDL_GameControllerOpen(i);
if (SDL_GameControllerGetAttached(pad) == 1) if (SDL_GameControllerGetAttached(pad) == 1) {
{
connected_controllers_.push_back(pad); connected_controllers_.push_back(pad);
const std::string name = SDL_GameControllerNameForIndex(i); const std::string name = SDL_GameControllerNameForIndex(i);
std::cout << "#" << i << ": " << name << std::endl; std::cout << "#" << i << ": " << name << std::endl;
controller_names_.push_back(name); controller_names_.push_back(name);
} } else {
else
{
std::cout << "SDL_GetError() = " << SDL_GetError() << std::endl; std::cout << "SDL_GetError() = " << SDL_GetError() << std::endl;
} }
} }
@@ -265,12 +212,9 @@ std::string Input::getControllerName(int controller_index) const { return num_ga
int Input::getNumControllers() const { return num_gamepads_; } int Input::getNumControllers() const { return num_gamepads_; }
// Obtiene el indice del controlador a partir de un event.id // Obtiene el indice del controlador a partir de un event.id
int Input::getJoyIndex(int id) const int Input::getJoyIndex(int id) const {
{ for (int i = 0; i < num_joysticks_; ++i) {
for (int i = 0; i < num_joysticks_; ++i) if (SDL_JoystickInstanceID(joysticks_[i]) == id) {
{
if (SDL_JoystickInstanceID(joysticks_[i]) == id)
{
return i; return i;
} }
} }
@@ -278,27 +222,23 @@ int Input::getJoyIndex(int id) const
} }
// Obtiene el SDL_GameControllerButton asignado a un input // Obtiene el SDL_GameControllerButton asignado a un input
SDL_GameControllerButton Input::getControllerBinding(int controller_index, InputAction input) const SDL_GameControllerButton Input::getControllerBinding(int controller_index, InputAction input) const {
{
return controller_bindings_[controller_index][static_cast<int>(input)].button; return controller_bindings_[controller_index][static_cast<int>(input)].button;
} }
// Obtiene el indice a partir del nombre del mando // Obtiene el indice a partir del nombre del mando
int Input::getIndexByName(const std::string &name) const int Input::getIndexByName(const std::string& name) const {
{
auto it = std::find(controller_names_.begin(), controller_names_.end(), name); auto it = std::find(controller_names_.begin(), controller_names_.end(), name);
return it != controller_names_.end() ? std::distance(controller_names_.begin(), it) : -1; return it != controller_names_.end() ? std::distance(controller_names_.begin(), it) : -1;
} }
// Comprueba el eje del mando // Comprueba el eje del mando
bool Input::checkAxisInput(InputAction input, int controller_index, bool repeat) bool Input::checkAxisInput(InputAction input, int controller_index, bool repeat) {
{
// Umbral para considerar el eje como activo // Umbral para considerar el eje como activo
const Sint16 threshold = 30000; const Sint16 threshold = 30000;
bool axis_active_now = false; bool axis_active_now = false;
switch (input) switch (input) {
{
case InputAction::LEFT: case InputAction::LEFT:
axis_active_now = SDL_GameControllerGetAxis(connected_controllers_[controller_index], SDL_CONTROLLER_AXIS_LEFTX) < -threshold; axis_active_now = SDL_GameControllerGetAxis(connected_controllers_[controller_index], SDL_CONTROLLER_AXIS_LEFTX) < -threshold;
break; break;
@@ -316,24 +256,18 @@ bool Input::checkAxisInput(InputAction input, int controller_index, bool repeat)
} }
// Referencia al binding correspondiente // Referencia al binding correspondiente
auto &binding = controller_bindings_.at(controller_index).at(static_cast<int>(input)); auto& binding = controller_bindings_.at(controller_index).at(static_cast<int>(input));
if (repeat) if (repeat) {
{
// Si se permite repetir, simplemente devolvemos el estado actual // Si se permite repetir, simplemente devolvemos el estado actual
return axis_active_now; return axis_active_now;
} } else {
else
{
// Si no se permite repetir, aplicamos la lógica de transición // Si no se permite repetir, aplicamos la lógica de transición
if (axis_active_now && !binding.axis_active) if (axis_active_now && !binding.axis_active) {
{
// Transición de inactivo a activo // Transición de inactivo a activo
binding.axis_active = true; binding.axis_active = true;
return true; return true;
} } else if (!axis_active_now && binding.axis_active) {
else if (!axis_active_now && binding.axis_active)
{
// Transición de activo a inactivo // Transición de activo a inactivo
binding.axis_active = false; binding.axis_active = false;
} }

View File

@@ -1,8 +1,9 @@
#pragma once #pragma once
#include <SDL2/SDL_gamecontroller.h> // Para SDL_GameControllerButton, SDL_G... #include <SDL3/SDL_gamecontroller.h> // Para SDL_GameControllerButton, SDL_G...
#include <SDL2/SDL_scancode.h> // Para SDL_Scancode #include <SDL3/SDL_scancode.h> // Para SDL_Scancode
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint8
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include <vector> // Para vector #include <vector> // Para vector
@@ -11,15 +12,13 @@ constexpr bool INPUT_ALLOW_REPEAT = true;
constexpr bool INPUT_DO_NOT_ALLOW_REPEAT = false; constexpr bool INPUT_DO_NOT_ALLOW_REPEAT = false;
// Tipos de entrada // Tipos de entrada
enum class InputDeviceToUse : int enum class InputDeviceToUse : int {
{
KEYBOARD = 0, KEYBOARD = 0,
CONTROLLER = 1, CONTROLLER = 1,
ANY = 2, ANY = 2,
}; };
enum class InputAction enum class InputAction {
{
// Inputs obligatorios // Inputs obligatorios
UP, UP,
DOWN, DOWN,
@@ -48,36 +47,36 @@ enum class InputAction
SIZE SIZE
}; };
class Input class Input {
{ private:
private:
// [SINGLETON] Objeto privado // [SINGLETON] Objeto privado
static Input *input_; static Input* input_;
struct KeyBindings struct KeyBindings {
{
Uint8 scancode; // Scancode asociado Uint8 scancode; // Scancode asociado
bool active; // Indica si está activo bool active; // Indica si está activo
// Constructor // Constructor
explicit KeyBindings(Uint8 sc = 0, bool act = false) explicit KeyBindings(Uint8 sc = 0, bool act = false)
: scancode(sc), active(act) {} : scancode(sc),
active(act) {}
}; };
struct ControllerBindings struct ControllerBindings {
{
SDL_GameControllerButton button; // GameControllerButton asociado SDL_GameControllerButton button; // GameControllerButton asociado
bool active; // Indica si está activo bool active; // Indica si está activo
bool axis_active; // Estado del eje bool axis_active; // Estado del eje
// Constructor // Constructor
explicit ControllerBindings(SDL_GameControllerButton btn = SDL_CONTROLLER_BUTTON_INVALID, bool act = false, bool axis_act = false) explicit ControllerBindings(SDL_GameControllerButton btn = SDL_CONTROLLER_BUTTON_INVALID, bool act = false, bool axis_act = false)
: button(btn), active(act), axis_active(axis_act) {} : button(btn),
active(act),
axis_active(axis_act) {}
}; };
// Variables // Variables
std::vector<SDL_GameController *> connected_controllers_; // Vector con todos los mandos conectados std::vector<SDL_GameController*> connected_controllers_; // Vector con todos los mandos conectados
std::vector<SDL_Joystick *> joysticks_; // Vector con todos los joysticks 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<KeyBindings> key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos
std::vector<std::vector<ControllerBindings>> controller_bindings_; // Vector con los botones asociadas a los inputs predefinidos para cada mando std::vector<std::vector<ControllerBindings>> controller_bindings_; // Vector con los botones asociadas a los inputs predefinidos para cada mando
std::vector<std::string> controller_names_; // Vector con los nombres de los mandos std::vector<std::string> controller_names_; // Vector con los nombres de los mandos
@@ -90,20 +89,20 @@ private:
bool checkAxisInput(InputAction input, int controller_index, bool repeat); bool checkAxisInput(InputAction input, int controller_index, bool repeat);
// Constructor // Constructor
explicit Input(const std::string &game_controller_db_path); explicit Input(const std::string& game_controller_db_path);
// Destructor // Destructor
~Input() = default; ~Input() = default;
public: public:
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
static void init(const std::string &game_controller_db_path); static void init(const std::string& game_controller_db_path);
// [SINGLETON] Destruiremos el objeto con esta función estática // [SINGLETON] Destruiremos el objeto con esta función estática
static void destroy(); static void destroy();
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
static Input *get(); static Input* get();
// Asigna inputs a teclas // Asigna inputs a teclas
void bindKey(InputAction input, SDL_Scancode code); void bindKey(InputAction input, SDL_Scancode code);
@@ -137,5 +136,5 @@ public:
SDL_GameControllerButton getControllerBinding(int controller_index, InputAction input) const; SDL_GameControllerButton getControllerBinding(int controller_index, InputAction input) const;
// Obtiene el indice a partir del nombre del mando // Obtiene el indice a partir del nombre del mando
int getIndexByName(const std::string &name) const; int getIndexByName(const std::string& name) const;
}; };

View File

@@ -1,14 +1,14 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect, SDL_Point #include <SDL3/SDL_rect.h> // Para SDL_Rect, SDL_Point
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
class SSprite; class SSprite;
struct ItemData struct ItemData {
{
std::string tile_set_file; // Ruta al fichero con los gráficos del item std::string tile_set_file; // Ruta al fichero con los gráficos del item
int x; // Posición del item en pantalla int x; // Posición del item en pantalla
int y; // Posición del item en pantalla int y; // Posición del item en pantalla
@@ -18,12 +18,17 @@ struct ItemData
Uint8 color2; // Uno de los dos colores que se utiliza para el item Uint8 color2; // Uno de los dos colores que se utiliza para el item
// Constructor // Constructor
ItemData() : x(0), y(0), tile(0), counter(0), color1(), color2() {} ItemData()
: x(0),
y(0),
tile(0),
counter(0),
color1(),
color2() {}
}; };
class Item class Item {
{ private:
private:
// Constantes // Constantes
static constexpr int ITEM_SIZE_ = 8; static constexpr int ITEM_SIZE_ = 8;
@@ -36,7 +41,7 @@ private:
SDL_Rect collider_; // Rectangulo de colisión SDL_Rect collider_; // Rectangulo de colisión
int change_color_speed; // Cuanto mas alto, mas tarda en cambiar de color int change_color_speed; // Cuanto mas alto, mas tarda en cambiar de color
public: public:
// Constructor // Constructor
explicit Item(ItemData item); explicit Item(ItemData item);
@@ -50,7 +55,7 @@ public:
void update() { counter_++; } void update() { counter_++; }
// Obtiene el rectangulo de colision del objeto // Obtiene el rectangulo de colision del objeto
SDL_Rect &getCollider() { return collider_; } SDL_Rect& getCollider() { return collider_; }
// Obtiene su ubicación // Obtiene su ubicación
SDL_Point getPos(); SDL_Point getPos();

View File

@@ -1,33 +1,31 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Point #include <SDL3/SDL_rect.h> // Para SDL_Point
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include <vector> // Para vector #include <vector> // Para vector
struct ItemTrackerData struct ItemTrackerData {
{
std::string name; // Nombre de la habitación donde se encuentra el objeto std::string name; // Nombre de la habitación donde se encuentra el objeto
std::vector<SDL_Point> pos; // Lista de objetos cogidos de la habitación std::vector<SDL_Point> pos; // Lista de objetos cogidos de la habitación
// Constructor // Constructor
ItemTrackerData(const std::string &name, const SDL_Point &position) ItemTrackerData(const std::string& name, const SDL_Point& position)
: name(name) : name(name) {
{
pos.push_back(position); pos.push_back(position);
} }
}; };
class ItemTracker class ItemTracker {
{ private:
private:
// [SINGLETON] Objeto privado // [SINGLETON] Objeto privado
static ItemTracker *item_tracker_; static ItemTracker* item_tracker_;
// Variables // Variables
std::vector<ItemTrackerData> item_list_; // Lista con todos los objetos recogidos std::vector<ItemTrackerData> item_list_; // Lista con todos los objetos recogidos
// Busca una entrada en la lista por nombre // Busca una entrada en la lista por nombre
int findByName(const std::string &name); int findByName(const std::string& name);
// Busca una entrada en la lista por posición // Busca una entrada en la lista por posición
int findByPos(int index, SDL_Point pos); int findByPos(int index, SDL_Point pos);
@@ -38,7 +36,7 @@ private:
// Destructor // Destructor
~ItemTracker() = default; ~ItemTracker() = default;
public: public:
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
static void init(); static void init();
@@ -46,11 +44,11 @@ public:
static void destroy(); static void destroy();
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
static ItemTracker *get(); static ItemTracker* get();
// Comprueba si el objeto ya ha sido cogido // Comprueba si el objeto ya ha sido cogido
bool hasBeenPicked(const std::string &name, SDL_Point pos); bool hasBeenPicked(const std::string& name, SDL_Point pos);
// Añade el objeto a la lista de objetos cogidos // Añade el objeto a la lista de objetos cogidos
void addItem(const std::string &name, SDL_Point pos); void addItem(const std::string& name, SDL_Point pos);
}; };

View File

@@ -1,294 +0,0 @@
#include "jail_shader.h"
#include <SDL2/SDL_rect.h> // Para SDL_Point
#include <SDL2/SDL_stdinc.h> // Para SDL_bool
#include <cstring> // Para strncmp
#include <iostream> // Para basic_ostream, operator<<, endl, cout
#include <stdexcept> // Para runtime_error
#include <vector> // Para vector
#ifdef __APPLE__
#include "CoreFoundation/CoreFoundation.h" // Para Core Foundation en macOS
#include <OpenGL/OpenGL.h> // Para OpenGL 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 <SDL2/SDL_opengl.h> // Para GLuint, glTexCoord2f, glVertex2f, GLfloat
#endif // __APPLE__
namespace shader
{
SDL_Window *win = nullptr;
SDL_Renderer *renderer = nullptr;
GLuint programId = 0;
SDL_Texture *backBuffer = nullptr;
SDL_Point win_size = {320 * 4, 256 * 4};
SDL_Point 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;
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");
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
glUseProgram;
}
#endif
// Función para compilar un shader a partir de un std::string
GLuint compileShader(const std::string &source, GLuint shaderType)
{
if (source.empty())
{
throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío.");
}
// Crear identificador del shader
GLuint resultado = glCreateShader(shaderType);
// Agregar una directiva según el tipo de shader
std::string directiva = (shaderType == GL_VERTEX_SHADER)
? "#define VERTEX\n"
: "#define FRAGMENT\n";
const char *sources[2] = {directiva.c_str(), source.c_str()};
// Especificar el código fuente del shader
glShaderSource(resultado, 2, sources, nullptr);
// Compilar el shader
glCompileShader(resultado);
// Verificar si la compilación fue exitosa
GLint compiladoCorrectamente = GL_FALSE;
glGetShaderiv(resultado, GL_COMPILE_STATUS, &compiladoCorrectamente);
if (compiladoCorrectamente != GL_TRUE)
{
std::cout << "Error en la compilación del shader (" << resultado << ")!" << std::endl;
GLint longitudLog;
glGetShaderiv(resultado, GL_INFO_LOG_LENGTH, &longitudLog);
if (longitudLog > 0)
{
std::vector<GLchar> log(longitudLog);
glGetShaderInfoLog(resultado, longitudLog, &longitudLog, log.data());
std::cout << "Registro de compilación del shader: " << log.data() << std::endl;
}
glDeleteShader(resultado);
resultado = 0;
}
return resultado;
}
// Función para compilar un programa de shaders (vertex y fragment) a partir de std::string
GLuint compileProgram(const std::string &vertexShaderSource, const std::string &fragmentShaderSource)
{
GLuint idPrograma = glCreateProgram();
// Si el fragment shader está vacío, reutilizamos el código del vertex shader
GLuint idShaderVertice = compileShader(vertexShaderSource, GL_VERTEX_SHADER);
GLuint idShaderFragmento = compileShader(fragmentShaderSource.empty() ? vertexShaderSource : fragmentShaderSource, GL_FRAGMENT_SHADER);
if (idShaderVertice && idShaderFragmento)
{
// Asociar los shaders al programa
glAttachShader(idPrograma, idShaderVertice);
glAttachShader(idPrograma, idShaderFragmento);
glLinkProgram(idPrograma);
glValidateProgram(idPrograma);
// Verificar el estado del enlace
GLint longitudLog;
glGetProgramiv(idPrograma, GL_INFO_LOG_LENGTH, &longitudLog);
if (longitudLog > 0)
{
std::vector<char> log(longitudLog);
glGetProgramInfoLog(idPrograma, longitudLog, &longitudLog, log.data());
std::cout << "Registro de información del programa:" << std::endl
<< log.data() << std::endl;
}
}
if (idShaderVertice)
{
glDeleteShader(idShaderVertice);
}
if (idShaderFragmento)
{
glDeleteShader(idShaderFragmento);
}
return idPrograma;
}
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader)
{
shader::win = ventana;
shader::renderer = SDL_GetRenderer(ventana);
shader::backBuffer = texturaBackBuffer;
SDL_GetWindowSize(ventana, &win_size.x, &win_size.y);
int acceso;
SDL_QueryTexture(texturaBackBuffer, nullptr, &acceso, &tex_size.x, &tex_size.y);
if (acceso != SDL_TEXTUREACCESS_TARGET)
{
throw std::runtime_error("ERROR FATAL: La textura debe tener definido SDL_TEXTUREACCESS_TARGET.");
}
SDL_RendererInfo infoRenderer;
SDL_GetRendererInfo(renderer, &infoRenderer);
// Verificar que el renderer sea OpenGL
if (!strncmp(infoRenderer.name, "opengl", 6))
{
#ifndef __APPLE__
if (!initGLExtensions())
{
std::cout << "ADVERTENCIA: No se han podido inicializar las extensiones de OpenGL." << std::endl;
usingOpenGL = false;
return false;
}
#endif
// Compilar el programa de shaders utilizando std::string
programId = compileProgram(vertexShader, fragmentShader);
}
else
{
std::cout << "ADVERTENCIA: El driver del renderer no es OpenGL." << std::endl;
usingOpenGL = false;
return false;
}
usingOpenGL = true;
return true;
}
void render()
{
GLint oldProgramId;
// Establece el color de fondo
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderClear(renderer);
if (usingOpenGL)
{
SDL_GL_BindTexture(backBuffer, nullptr, nullptr);
if (programId != 0)
{
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
glUseProgram(programId);
}
// Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize
int logicalW, logicalH;
SDL_RenderGetLogicalSize(renderer, &logicalW, &logicalH);
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;
SDL_bool useIntegerScale = SDL_RenderGetIntegerScale(renderer);
if (useIntegerScale)
{
// 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;
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;
}
}
glViewport(viewportX, viewportY, viewportW, viewportH);
// 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();
SDL_GL_SwapWindow(win);
if (programId != 0)
{
glUseProgram(oldProgramId);
}
}
else
{
SDL_RenderCopy(renderer, backBuffer, nullptr, nullptr);
SDL_RenderPresent(renderer);
}
}
}

View File

@@ -1,228 +0,0 @@
#include "loading_screen.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
#include <stdlib.h> // Para rand
#include "defines.h" // Para GAME_SPEED
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "jail_audio.h" // Para JA_PlayMusic, JA_SetVolume, JA_StopMusic
#include "options.h" // Para Options, options, SectionState, Options...
#include "resource.h" // Para Resource
#include "s_sprite.h" // Para SSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "utils.h" // Para stringToColor, PaletteColor
// Constructor
LoadingScreen::LoadingScreen()
: mono_loading_screen_surface_(Resource::get()->getSurface("loading_screen_bn.gif")),
color_loading_screen_surface_(Resource::get()->getSurface("loading_screen_color.gif")),
mono_loading_screen_sprite_(std::make_shared<SSprite>(mono_loading_screen_surface_, 0, 0, mono_loading_screen_surface_->getWidth(), mono_loading_screen_surface_->getHeight())),
color_loading_screen_sprite_(std::make_shared<SSprite>(color_loading_screen_surface_, 0, 0, color_loading_screen_surface_->getWidth(), color_loading_screen_surface_->getHeight())),
screen_surface_(std::make_shared<Surface>(options.game.width, options.game.height))
{
// Configura la superficie donde se van a pintar los sprites
screen_surface_->clear(static_cast<Uint8>(PaletteColor::WHITE));
// Inicializa variables
options.section.section = Section::LOADING_SCREEN;
options.section.subsection = Subsection::NONE;
// Establece el orden de las lineas para imitar el direccionamiento de memoria del spectrum
for (int i = 0; i < 192; ++i)
{
if (i < 64)
{ // Primer bloque de 2K
line_index_[i] = ((i % 8) * 8) + (i / 8);
}
else if (i < 128)
{ // Segundo bloque de 2K
line_index_[i] = 64 + ((i % 8) * 8) + ((i - 64) / 8);
}
else
{ // Tercer bloque de 2K
line_index_[i] = 128 + ((i % 8) * 8) + ((i - 128) / 8);
}
}
// Cambia el color del borde
Screen::get()->setBorderColor(stringToColor("black"));
}
// Destructor
LoadingScreen::~LoadingScreen()
{
JA_StopMusic();
}
// Comprueba el manejador de eventos
void LoadingScreen::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
globalEvents::check(event);
}
}
// Comprueba las entradas
void LoadingScreen::checkInput()
{
globalInputs::check();
}
// Gestiona el contador de carga
void LoadingScreen::updateLoad()
{
// Primera parte de la carga, la parte en blanco y negro
if (loading_first_part_)
{
// Cada 5 pasos el load_counter_ se incrementa en uno
constexpr int NUM_STEPS = 5;
constexpr int STEPS = 51;
load_counter_ = counter_ / NUM_STEPS;
if (load_counter_ < 192)
{
load_rect_.x = STEPS * (counter_ % NUM_STEPS);
load_rect_.y = line_index_[load_counter_];
mono_loading_screen_sprite_->setClip(load_rect_);
mono_loading_screen_sprite_->setPosition(load_rect_);
}
// Una vez actualizadas las 192 lineas, pasa a la segunda fase de la carga
else if (load_counter_ == 192)
{
loading_first_part_ = false;
load_counter_ = 0;
load_rect_ = {0, 0, 16, 8};
color_loading_screen_sprite_->setClip(load_rect_);
color_loading_screen_sprite_->setPosition(load_rect_);
JA_PlayMusic(Resource::get()->getMusic("loading_sound3.ogg"));
}
}
// Segunda parte de la carga, la parte de los bloques en color
else
{
load_counter_ += 2;
load_rect_.x = (load_counter_ * 8) % 256;
load_rect_.y = (load_counter_ / 32) * 8;
color_loading_screen_sprite_->setClip(load_rect_);
color_loading_screen_sprite_->setPosition(load_rect_);
// Comprueba si ha terminado la intro
if (load_counter_ >= 768)
{
options.section.section = Section::TITLE;
options.section.subsection = Subsection::TITLE_WITH_LOADING_SCREEN;
JA_StopMusic();
}
}
}
// Gestiona el contador interno
void LoadingScreen::updateCounter()
{
(pre_counter_ >= 50) ? counter_++ : pre_counter_++;
if (counter_ == 1)
{
JA_PlayMusic(Resource::get()->getMusic("loading_sound2.ogg"));
}
}
// Dibuja la pantalla de carga
void LoadingScreen::renderLoad()
{
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(screen_surface_);
loading_first_part_ ? mono_loading_screen_sprite_->render(1, stringToColor("black")) : color_loading_screen_sprite_->render();
Screen::get()->setRendererSurface(previuos_renderer);
}
// Dibuja el efecto de carga en el borde
void LoadingScreen::renderBorder()
{
// Obtiene la Surface del borde
auto border = Screen::get()->getBorderSurface();
// Pinta el borde de color azul
border->clear(static_cast<Uint8>(PaletteColor::BLUE));
// Añade lineas amarillas
const Uint8 COLOR = static_cast<Uint8>(PaletteColor::YELLOW);
const int WIDTH = options.game.width + (options.video.border.width * 2);
const int HEIGHT = options.game.height + (options.video.border.height * 2);
bool draw_enabled = rand() % 2 == 0 ? true : false;
int row = 0;
while (row < HEIGHT)
{
const int ROW_HEIGHT = (rand() % 4) + 3;
if (draw_enabled)
{
for (int i = row; i < row + ROW_HEIGHT; ++i)
{
border->drawLine(0, i, WIDTH, i, COLOR);
}
}
row += ROW_HEIGHT;
draw_enabled = !draw_enabled;
}
}
// Actualiza las variables
void LoadingScreen::update()
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED)
{
ticks_ = SDL_GetTicks();
checkInput();
updateCounter();
updateLoad();
renderLoad();
Screen::get()->update();
}
}
// Dibuja en pantalla
void LoadingScreen::render()
{
if (options.video.border.enabled)
{
// Dibuja el efecto de carga en el borde
renderBorder();
}
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
Screen::get()->clearSurface(stringToColor("white"));
// Copia la surface a la surface de Screen
screen_surface_->render(0, 0);
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void LoadingScreen::run()
{
// Inicia el sonido de carga
JA_SetVolume(64);
JA_PlayMusic(Resource::get()->getMusic("loading_sound1.ogg"));
// Limpia la pantalla
Screen::get()->start();
Screen::get()->clearRenderer();
Screen::get()->render();
while (options.section.section == Section::LOADING_SCREEN)
{
update();
checkEvents();
render();
}
JA_SetVolume(128);
}

View File

@@ -1,61 +0,0 @@
#pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_stdinc.h> // Para Uint32
#include <memory> // Para shared_ptr
class SSprite; // lines 7-7
class Surface; // lines 8-8
class LoadingScreen
{
private:
// Objetos y punteros
std::shared_ptr<Surface> mono_loading_screen_surface_; // Surface con la pantalla de carga en blanco y negro
std::shared_ptr<Surface> color_loading_screen_surface_; // Surface con la pantalla de carga en color
std::shared_ptr<SSprite> mono_loading_screen_sprite_; // SSprite para manejar la textura loadingScreenTexture1
std::shared_ptr<SSprite> color_loading_screen_sprite_; // SSprite para manejar la textura loadingScreenTexture2
std::shared_ptr<Surface> screen_surface_; // Surface para dibujar la pantalla de carga
// Variables
int pre_counter_ = 0; // Contador previo para realizar una pausa inicial
int counter_ = 0; // Contador
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
int load_counter_ = 0; // Contador para controlar las cargas
bool loading_first_part_ = true; // Para saber en que parte de la carga se encuentra
int line_index_[192]; // El orden en el que se procesan las 192 lineas de la pantalla de carga
SDL_Rect load_rect_ = {0, 0, 52, 1}; // Rectangulo para dibujar la pantalla de carga
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Gestiona el contador interno
void updateCounter();
// Gestiona el contador de carga
void updateLoad();
// Dibuja la pantalla de carga
void renderLoad();
// Dibuja el efecto de carga en el borde
void renderBorder();
public:
// Constructor
LoadingScreen();
// Destructor
~LoadingScreen();
// Bucle principal
void run();
};

View File

@@ -1,265 +0,0 @@
#include "logo.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
#include "defines.h" // Para GAME_SPEED
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "options.h" // Para Options, SectionState, options, Section
#include "resource.h" // Para Resource
#include "s_sprite.h" // Para SSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "utils.h" // Para PaletteColor
// Constructor
Logo::Logo()
: jailgames_surface_(Resource::get()->getSurface("jailgames.gif")),
since_1998_surface_(Resource::get()->getSurface("since_1998.gif")),
since_1998_sprite_(std::make_shared<SSprite>(since_1998_surface_, (256 - since_1998_surface_->getWidth()) / 2, 83 + jailgames_surface_->getHeight() + 5, since_1998_surface_->getWidth(), since_1998_surface_->getHeight()))
{
since_1998_sprite_->setClip(0, 0, since_1998_surface_->getWidth(), since_1998_surface_->getHeight());
since_1998_color_ = static_cast<Uint8>(PaletteColor::BLACK);
jailgames_color_ = static_cast<Uint8>(PaletteColor::BRIGHT_WHITE);
// Crea los sprites de cada linea
for (int i = 0; i < jailgames_surface_->getHeight(); ++i)
{
jailgames_sprite_.push_back(std::make_shared<SSprite>(jailgames_surface_, 0, i, jailgames_surface_->getWidth(), 1));
jailgames_sprite_.back()->setClip(0, i, jailgames_surface_->getWidth(), 1);
jailgames_sprite_.at(i)->setX((i % 2 == 0) ? (256 + (i * 3)) : (-181 - (i * 3)));
jailgames_sprite_.at(i)->setY(83 + i);
}
// Inicializa variables
options.section.section = Section::LOGO;
// Inicializa el vector de colores
const std::vector<Uint8> COLORS = {
static_cast<Uint8>(PaletteColor::BLACK),
static_cast<Uint8>(PaletteColor::BLUE),
static_cast<Uint8>(PaletteColor::RED),
static_cast<Uint8>(PaletteColor::MAGENTA),
static_cast<Uint8>(PaletteColor::GREEN),
static_cast<Uint8>(PaletteColor::CYAN),
static_cast<Uint8>(PaletteColor::YELLOW),
static_cast<Uint8>(PaletteColor::BRIGHT_WHITE)};
for (const auto &color : COLORS)
{
color_.push_back(color);
}
// Cambia el color del borde
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
}
// Comprueba el manejador de eventos
void Logo::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
globalEvents::check(event);
}
}
// Comprueba las entradas
void Logo::checkInput()
{
globalInputs::check();
}
// Gestiona el logo de JAILGAME
void Logo::updateJAILGAMES()
{
if (counter_ > 30)
{
for (int i = 1; i < (int)jailgames_sprite_.size(); ++i)
{
constexpr int SPEED = 8;
constexpr int DEST = 37;
if (jailgames_sprite_.at(i)->getX() != 37)
{
if (i % 2 == 0)
{
jailgames_sprite_.at(i)->incX(-SPEED);
if (jailgames_sprite_.at(i)->getX() < DEST)
{
jailgames_sprite_.at(i)->setX(DEST);
}
}
else
{
jailgames_sprite_.at(i)->incX(SPEED);
if (jailgames_sprite_.at(i)->getX() > DEST)
{
jailgames_sprite_.at(i)->setX(DEST);
}
}
}
}
}
}
// Gestiona el color de las texturas
void Logo::updateTextureColors()
{
constexpr int INI = 70;
constexpr int INC = 4;
if (counter_ == INI + INC * 0)
{
since_1998_color_ = color_.at(0);
}
else if (counter_ == INI + INC * 1)
{
since_1998_color_ = color_.at(1);
}
else if (counter_ == INI + INC * 2)
{
since_1998_color_ = color_.at(2);
}
else if (counter_ == INI + INC * 3)
{
since_1998_color_ = color_.at(3);
}
else if (counter_ == INI + INC * 4)
{
since_1998_color_ = color_.at(4);
}
else if (counter_ == INI + INC * 5)
{
since_1998_color_ = color_.at(5);
}
else if (counter_ == INI + INC * 6)
{
since_1998_color_ = color_.at(6);
}
else if (counter_ == INI + INC * 7)
{
since_1998_color_ = color_.at(7);
}
else if (counter_ == INIT_FADE_ + INC * 0)
{
jailgames_color_ = color_.at(6);
since_1998_color_ = color_.at(6);
}
else if (counter_ == INIT_FADE_ + INC * 1)
{
jailgames_color_ = color_.at(5);
since_1998_color_ = color_.at(5);
}
else if (counter_ == INIT_FADE_ + INC * 2)
{
jailgames_color_ = color_.at(4);
since_1998_color_ = color_.at(4);
}
else if (counter_ == INIT_FADE_ + INC * 3)
{
jailgames_color_ = color_.at(3);
since_1998_color_ = color_.at(3);
}
else if (counter_ == INIT_FADE_ + INC * 4)
{
jailgames_color_ = color_.at(2);
since_1998_color_ = color_.at(2);
}
else if (counter_ == INIT_FADE_ + INC * 5)
{
jailgames_color_ = color_.at(1);
since_1998_color_ = color_.at(1);
}
else if (counter_ == INIT_FADE_ + INC * 6)
{
jailgames_color_ = color_.at(0);
since_1998_color_ = color_.at(0);
}
}
// Actualiza las variables
void Logo::update()
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED)
{
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba las entradas
checkInput();
// Incrementa el contador
counter_++;
// Gestiona el logo de JAILGAME
updateJAILGAMES();
// Gestiona el color de las texturas
updateTextureColors();
// Actualiza el objeto Screen
Screen::get()->update();
// Comprueba si ha terminado el logo
if (counter_ == END_LOGO_ + POST_LOGO_)
{
endSection();
}
}
}
// Dibuja en pantalla
void Logo::render()
{
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));
// Dibuja los objetos
for (const auto &s : jailgames_sprite_)
{
s->render(1, jailgames_color_);
}
since_1998_sprite_->render(1, since_1998_color_);
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void Logo::run()
{
while (options.section.section == Section::LOGO)
{
update();
checkEvents();
render();
}
}
// Termina la sección
void Logo::endSection()
{
if (options.section.subsection == Subsection::LOGO_TO_TITLE)
{
options.section.section = Section::TITLE;
}
else if (options.section.subsection == Subsection::LOGO_TO_INTRO)
{
options.section.section = Section::LOADING_SCREEN;
}
}

View File

@@ -1,60 +0,0 @@
#pragma once
#include <SDL2/SDL_stdinc.h> // Para Uint8, Uint32
#include <memory> // Para shared_ptr
#include <vector> // Para vector
class SSprite; // lines 7-7
class Surface; // lines 8-8
class Logo
{
private:
// Constantes
static constexpr int INIT_FADE_ = 300; // Tiempo del contador cuando inicia el fade a negro
static constexpr int END_LOGO_ = 400; // Tiempo del contador para terminar el logo
static constexpr int POST_LOGO_ = 20; // Tiempo que dura el logo con el fade al maximo
// Objetos y punteros
std::shared_ptr<Surface> jailgames_surface_; // Textura con los graficos "JAILGAMES"
std::shared_ptr<Surface> since_1998_surface_; // Textura con los graficos "Since 1998"
std::vector<std::shared_ptr<SSprite>> jailgames_sprite_; // Vector con los sprites de cada linea que forman el bitmap JAILGAMES
std::shared_ptr<SSprite> since_1998_sprite_; // SSprite para manejar la textura2
Uint8 jailgames_color_ = 0; // Color para el sprite de "JAILGAMES"
Uint8 since_1998_color_ = 0; // Color para el sprite de "Since 1998"
// Variables
std::vector<Uint8> color_; // Vector con los colores para el fade
int counter_ = 0; // Contador
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Gestiona el logo de JAILGAME
void updateJAILGAMES();
// Gestiona el color de las texturas
void updateTextureColors();
// Termina la sección
void endSection();
public:
// Constructor
Logo();
// Destructor
~Logo() = default;
// Bucle principal
void run();
};

View File

@@ -1,33 +1,28 @@
#include "mouse.h" #include "mouse.h"
#include <SDL2/SDL_mouse.h> // Para SDL_ShowCursor
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
namespace Mouse #include <SDL3/SDL_mouse.h> // Para SDL_ShowCursor
{ #include <SDL3/SDL_timer.h> // Para SDL_GetTicks
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) namespace Mouse {
{ Uint32 cursor_hide_time = 3000; // Tiempo en milisegundos para ocultar el cursor
if (event.type == SDL_MOUSEMOTION) 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_MOUSEMOTION) {
last_mouse_move_time = SDL_GetTicks(); last_mouse_move_time = SDL_GetTicks();
if (!cursor_visible) if (!cursor_visible) {
{
SDL_ShowCursor(SDL_ENABLE); SDL_ShowCursor(SDL_ENABLE);
cursor_visible = true; cursor_visible = true;
} }
} }
} }
void updateCursorVisibility() void updateCursorVisibility() {
{
Uint32 current_time = SDL_GetTicks(); Uint32 current_time = SDL_GetTicks();
if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time)) if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time)) {
{
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
cursor_visible = false; cursor_visible = false;
} }
}
} }
} // namespace Mouse

View File

@@ -1,14 +1,13 @@
#pragma once #pragma once
#include <SDL2/SDL_events.h> // Para SDL_Event #include <SDL3/SDL_events.h> // Para SDL_Event
#include <SDL2/SDL_stdinc.h> // Para Uint32 #include <SDL3/SDL_stdinc.h> // Para Uint32
namespace Mouse namespace Mouse {
{ extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor
extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor extern Uint32 last_mouse_move_time; // Última vez que el ratón se movió
extern Uint32 last_mouse_move_time; // Última vez que el ratón se movió extern bool cursor_visible; // Estado del cursor
extern bool cursor_visible; // Estado del cursor
void handleEvent(const SDL_Event &event); void handleEvent(const SDL_Event& event);
void updateCursorVisibility(); void updateCursorVisibility();
} } // namespace Mouse

View File

@@ -1,131 +0,0 @@
#pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_stdinc.h> // Para Uint32, Uint8
#include <memory> // Para shared_ptr
#include <string> // Para string, basic_string
#include <vector> // Para vector
class SSprite; // lines 8-8
class Surface; // lines 10-10
class Text; // lines 9-9
// Constantes
constexpr Uint32 DEFAULT_NOTIFICATION_DURATION = 2000;
constexpr Uint32 CHEEVO_NOTIFICATION_DURATION = 4000;
// Justificado para las notificaciones
enum class NotificationText
{
LEFT,
CENTER,
};
class Notifier
{
private:
// Constantes
static constexpr int ICON_SIZE_ = 16;
static constexpr int PADDING_OUT_ = 0;
// [SINGLETON] Objeto notifier
static Notifier *notifier_;
enum class NotificationStatus
{
RISING,
STAY,
VANISHING,
FINISHED,
};
enum class NotificationShape
{
ROUNDED,
SQUARED,
};
struct Notification
{
std::shared_ptr<Surface> surface; // Superficie asociada a la notificación
std::shared_ptr<SSprite> sprite; // Sprite asociado para gráficos o animaciones
std::vector<std::string> texts; // Lista de textos incluidos en la notificación
NotificationStatus state; // Estado actual de la notificación (RISING, SHOWING, etc.)
NotificationShape shape; // Forma de la notificación (ej. SQUARED o ROUNDED)
SDL_Rect rect; // Dimensiones y posición de la notificación en pantalla
int y; // Posición actual en el eje Y
int travel_dist; // Distancia a recorrer (por ejemplo, en animaciones)
std::string code; // Código identificador único para esta notificación
bool can_be_removed; // Indica si la notificación puede ser eliminada
int height; // Altura de la notificación
Uint32 start_time; // Momento en que se creó la notificación
Uint32 elapsed_time; // Tiempo transcurrido desde la creación
Uint32 display_duration; // Duración total para mostrar la notificación
// Constructor
explicit Notification()
: surface(nullptr), // Inicializar superficie como nula
sprite(nullptr), // Inicializar sprite como nulo
texts(), // Inicializar lista de textos vacía
state(NotificationStatus::RISING), // Estado inicial como "RISING"
shape(NotificationShape::SQUARED), // Forma inicial como "SQUARED"
rect{0, 0, 0, 0}, // Rectángulo inicial vacío
y(0), // Posición Y inicializada a 0
travel_dist(0), // Distancia inicializada a 0
code(""), // Código identificador vacío
can_be_removed(true), // Inicialmente se puede eliminar
height(0), // Altura inicializada a 0
start_time(0), // Tiempo de creación inicializado a 0
elapsed_time(0), // Tiempo transcurrido inicializado a 0
display_duration(0) // Duración inicializada a 0
{
}
};
std::shared_ptr<Surface> icon_surface_; // Textura para los iconos de las notificaciones
std::shared_ptr<Text> text_; // Objeto para dibujar texto
// Variables
Uint8 bg_color_; // Color de fondo de las notificaciones
std::vector<Notification> notifications_; // La lista de notificaciones activas
bool stack_; // Indica si las notificaciones se apilan
bool has_icons_; // Indica si el notificador tiene textura para iconos
// Elimina las notificaciones finalizadas
void clearFinishedNotifications();
// Finaliza y elimnina todas las notificaciones activas
void clearNotifications();
// [SINGLETON] Ahora el constructor y el destructor son privados, para no poder crear objetos notifier desde fuera
// Constructor
Notifier(const std::string &icon_file, const std::string &text);
// Destructor
~Notifier() = default;
public:
// [SINGLETON] Crearemos el objeto con esta función estática
static void init(const std::string &icon_file, const std::string &text);
// [SINGLETON] Destruiremos el objeto con esta función estática
static void destroy();
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
static Notifier *get();
// Dibuja las notificaciones por pantalla
void render();
// Actualiza el estado de las notificaiones
void update();
// Muestra una notificación de texto por pantalla
void show(std::vector<std::string> texts, NotificationText text_is = NotificationText::LEFT, Uint32 display_duration = DEFAULT_NOTIFICATION_DURATION, int icon = -1, bool can_be_removed = true, const std::string &code = std::string());
// Indica si hay notificaciones activas
bool isActive();
// Obtiene los códigos de las notificaciones
std::vector<std::string> getCodes();
};

View File

@@ -1,5 +1,7 @@
#include "options.h" #include "options.h"
#include <SDL2/SDL_video.h> // Para SDL_WINDOW_FULLSCREEN_DESKTOP
#include <SDL3/SDL_video.h> // Para SDL_WINDOW_FULLSCREEN_DESKTOP
#include <algorithm> // Para find_if #include <algorithm> // Para find_if
#include <cctype> // Para isspace #include <cctype> // Para isspace
#include <fstream> // Para basic_ostream, operator<<, basic_ofstream #include <fstream> // Para basic_ostream, operator<<, basic_ofstream
@@ -13,11 +15,10 @@
// Variables // Variables
Options options; Options options;
bool setOptions(const std::string &var, const std::string &value); bool setOptions(const std::string& var, const std::string& value);
// Crea e inicializa las opciones del programa // Crea e inicializa las opciones del programa
void initOptions() void initOptions() {
{
options = Options(); options = Options();
#ifdef DEBUG #ifdef DEBUG
@@ -30,8 +31,7 @@ void initOptions()
} }
// Carga las opciones desde un fichero // Carga las opciones desde un fichero
bool loadOptionsFromFile(const std::string &file_path) bool loadOptionsFromFile(const std::string& file_path) {
{
// Indicador de éxito en la carga // Indicador de éxito en la carga
bool success = true; bool success = true;
@@ -43,28 +43,22 @@ bool loadOptionsFromFile(const std::string &file_path)
std::ifstream file(file_path); std::ifstream file(file_path);
// Si el fichero se puede abrir // Si el fichero se puede abrir
if (file.good()) if (file.good()) {
{
// Procesa el fichero línea a línea // Procesa el fichero línea a línea
if (options.console) if (options.console) {
{
std::cout << "Reading file config.txt\n"; std::cout << "Reading file config.txt\n";
} }
std::string line; std::string line;
while (std::getline(file, line)) while (std::getline(file, line)) {
{
// Elimina espacios en blanco iniciales y finales // Elimina espacios en blanco iniciales y finales
line = std::string(std::find_if(line.begin(), line.end(), [](int ch) line = std::string(std::find_if(line.begin(), line.end(), [](int ch) { return !std::isspace(ch); }),
{ return !std::isspace(ch); }),
line.end()); line.end());
line.erase(std::find_if(line.rbegin(), line.rend(), [](int ch) line.erase(std::find_if(line.rbegin(), line.rend(), [](int ch) { return !std::isspace(ch); })
{ return !std::isspace(ch); })
.base(), .base(),
line.end()); line.end());
// Ignora líneas vacías o comentarios // Ignora líneas vacías o comentarios
if (line.empty() || line[0] == '#') if (line.empty() || line[0] == '#') {
{
continue; continue;
} }
@@ -72,12 +66,9 @@ bool loadOptionsFromFile(const std::string &file_path)
std::istringstream iss(line); std::istringstream iss(line);
std::string key, value; std::string key, value;
if (iss >> key >> value) if (iss >> key >> value) {
{ if (!setOptions(key, value)) {
if (!setOptions(key, value)) if (options.console) {
{
if (options.console)
{
std::cout << "Warning: file config.txt\n"; std::cout << "Warning: file config.txt\n";
std::cout << "unknown parameter " << key << std::endl; std::cout << "unknown parameter " << key << std::endl;
} }
@@ -87,25 +78,20 @@ bool loadOptionsFromFile(const std::string &file_path)
} }
// Cierra el fichero // Cierra el fichero
if (options.console) if (options.console) {
{
std::cout << "Closing file config.txt\n\n"; std::cout << "Closing file config.txt\n\n";
} }
file.close(); file.close();
} } else {
else
{
// Crea el fichero con los valores por defecto // Crea el fichero con los valores por defecto
saveOptionsToFile(file_path); saveOptionsToFile(file_path);
} }
// Si la versión de fichero no coincide, crea un fichero nuevo con los valores por defecto // Si la versión de fichero no coincide, crea un fichero nuevo con los valores por defecto
if (configVersion != options.version) if (configVersion != options.version) {
{
initOptions(); initOptions();
saveOptionsToFile(file_path); saveOptionsToFile(file_path);
if (options.console) if (options.console) {
{
std::cout << "Wrong config file: initializing options.\n\n"; std::cout << "Wrong config file: initializing options.\n\n";
} }
} }
@@ -114,23 +100,20 @@ bool loadOptionsFromFile(const std::string &file_path)
} }
// Guarda las opciones en un fichero // Guarda las opciones en un fichero
bool saveOptionsToFile(const std::string &file_path) bool saveOptionsToFile(const std::string& file_path) {
{
// Crea y abre el fichero de texto // Crea y abre el fichero de texto
std::ofstream file(file_path); std::ofstream file(file_path);
bool success = file.is_open(); // Verifica si el archivo se abrió correctamente bool success = file.is_open(); // Verifica si el archivo se abrió correctamente
if (!success) // Si no se pudo abrir el archivo, muestra un mensaje de error y devuelve false if (!success) // Si no se pudo abrir el archivo, muestra un mensaje de error y devuelve false
{ {
if (options.console) if (options.console) {
{
std::cerr << "Error: Unable to open file " << file_path << " for writing." << std::endl; std::cerr << "Error: Unable to open file " << file_path << " for writing." << std::endl;
} }
return false; return false;
} }
if (options.console) if (options.console) {
{
std::cout << file_path << " open for writing" << std::endl; std::cout << file_path << " open for writing" << std::endl;
} }
@@ -174,101 +157,68 @@ bool saveOptionsToFile(const std::string &file_path)
return success; return success;
} }
bool setOptions(const std::string &var, const std::string &value) bool setOptions(const std::string& var, const std::string& value) {
{ static const std::unordered_map<std::string, std::function<void(const std::string&)>> optionHandlers = {
static const std::unordered_map<std::string, std::function<void(const std::string &)>> optionHandlers = { {"version", [](const std::string& v) { options.version = v; }},
{"version", [](const std::string &v) {"keys", [](const std::string& v) {
{ options.version = v; }},
{"keys", [](const std::string &v)
{
int val = safeStoi(v, static_cast<int>(DEFAULT_CONTROL_SCHEME)); int val = safeStoi(v, static_cast<int>(DEFAULT_CONTROL_SCHEME));
if (val == static_cast<int>(ControlScheme::CURSOR) || val == static_cast<int>(ControlScheme::OPQA) || val == static_cast<int>(ControlScheme::WASD)) if (val == static_cast<int>(ControlScheme::CURSOR) || val == static_cast<int>(ControlScheme::OPQA) || val == static_cast<int>(ControlScheme::WASD)) {
{
options.keys = static_cast<ControlScheme>(val); options.keys = static_cast<ControlScheme>(val);
} } else {
else
{
options.keys = DEFAULT_CONTROL_SCHEME; options.keys = DEFAULT_CONTROL_SCHEME;
} }
}}, }},
{"window.zoom", [](const std::string &v) {"window.zoom", [](const std::string& v) {
{
int val = safeStoi(v, DEFAULT_WINDOW_ZOOM); int val = safeStoi(v, DEFAULT_WINDOW_ZOOM);
if (val > 0) if (val > 0) {
{
options.window.zoom = val; options.window.zoom = val;
} } else {
else
{
options.window.zoom = DEFAULT_WINDOW_ZOOM; options.window.zoom = DEFAULT_WINDOW_ZOOM;
} }
}}, }},
{"video.mode", [](const std::string &v) {"video.mode", [](const std::string& v) {
{
int val = safeStoi(v, 0); int val = safeStoi(v, 0);
if (val == 0 || val == SDL_WINDOW_FULLSCREEN_DESKTOP) if (val == 0 || val == SDL_WINDOW_FULLSCREEN_DESKTOP) {
{
options.video.mode = val; options.video.mode = val;
} } else {
else
{
options.video.mode = 0; options.video.mode = 0;
} }
}}, }},
{"video.filter", [](const std::string &v) {"video.filter", [](const std::string& v) {
{
int val = safeStoi(v, static_cast<int>(DEFAULT_VIDEO_FILTER)); int val = safeStoi(v, static_cast<int>(DEFAULT_VIDEO_FILTER));
if (val == static_cast<int>(ScreenFilter::NEAREST) || val == static_cast<int>(ScreenFilter::LINEAR)) if (val == static_cast<int>(ScreenFilter::NEAREST) || val == static_cast<int>(ScreenFilter::LINEAR)) {
{
options.video.filter = static_cast<ScreenFilter>(val); options.video.filter = static_cast<ScreenFilter>(val);
} } else {
else
{
options.video.filter = DEFAULT_VIDEO_FILTER; options.video.filter = DEFAULT_VIDEO_FILTER;
} }
}}, }},
{"video.shaders", [](const std::string &v) {"video.shaders", [](const std::string& v) { options.video.shaders = stringToBool(v); }},
{ options.video.shaders = stringToBool(v); }}, {"video.vertical_sync", [](const std::string& v) { options.video.vertical_sync = stringToBool(v); }},
{"video.vertical_sync", [](const std::string &v) {"video.integer_scale", [](const std::string& v) { options.video.integer_scale = stringToBool(v); }},
{ options.video.vertical_sync = stringToBool(v); }}, {"video.keep_aspect", [](const std::string& v) { options.video.keep_aspect = stringToBool(v); }},
{"video.integer_scale", [](const std::string &v) {"video.border.enabled", [](const std::string& v) { options.video.border.enabled = stringToBool(v); }},
{ options.video.integer_scale = stringToBool(v); }}, {"video.border.width", [](const std::string& v) {
{"video.keep_aspect", [](const std::string &v)
{ options.video.keep_aspect = stringToBool(v); }},
{"video.border.enabled", [](const std::string &v)
{ options.video.border.enabled = stringToBool(v); }},
{"video.border.width", [](const std::string &v)
{
int val = safeStoi(v, DEFAULT_BORDER_WIDTH); int val = safeStoi(v, DEFAULT_BORDER_WIDTH);
if (val > 0) if (val > 0) {
{
options.video.border.width = val; options.video.border.width = val;
} } else {
else
{
options.video.border.width = DEFAULT_BORDER_WIDTH; options.video.border.width = DEFAULT_BORDER_WIDTH;
} }
}}, }},
{"video.border.height", [](const std::string &v) {"video.border.height", [](const std::string& v) {
{
int val = safeStoi(v, DEFAULT_BORDER_HEIGHT); int val = safeStoi(v, DEFAULT_BORDER_HEIGHT);
if (val > 0) if (val > 0) {
{
options.video.border.height = val; options.video.border.height = val;
} } else {
else
{
options.video.border.height = DEFAULT_BORDER_HEIGHT; options.video.border.height = DEFAULT_BORDER_HEIGHT;
} }
}}, }},
{"video.palette", [](const std::string &v) {"video.palette", [](const std::string& v) {
{
options.video.palette = v; options.video.palette = v;
}}}; }}};
auto it = optionHandlers.find(var); auto it = optionHandlers.find(var);
if (it != optionHandlers.end()) if (it != optionHandlers.end()) {
{
it->second(value); it->second(value);
return true; return true;
} }

View File

@@ -1,14 +1,15 @@
#pragma once #pragma once
#include <SDL2/SDL_stdinc.h> // Para Uint32 #include <SDL3/SDL_stdinc.h> // Para Uint32
#include <algorithm>
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include "screen.h" // Para ScreenFilter #include "screen.h" // Para ScreenFilter
#include "utils.h" // Para Color, Palette #include "utils.h" // Para Color, Palette
#include <algorithm>
// Secciones del programa // Secciones del programa
enum class Section enum class Section {
{
LOGO, LOGO,
LOADING_SCREEN, LOADING_SCREEN,
TITLE, TITLE,
@@ -22,8 +23,7 @@ enum class Section
}; };
// Subsecciones // Subsecciones
enum class Subsection enum class Subsection {
{
NONE, NONE,
LOGO_TO_INTRO, LOGO_TO_INTRO,
LOGO_TO_TITLE, LOGO_TO_TITLE,
@@ -32,8 +32,7 @@ enum class Subsection
}; };
// Posiciones de las notificaciones // Posiciones de las notificaciones
enum class NotificationPosition enum class NotificationPosition {
{
UPPER_LEFT, UPPER_LEFT,
UPPER_CENTER, UPPER_CENTER,
UPPER_RIGHT, UPPER_RIGHT,
@@ -49,8 +48,7 @@ enum class NotificationPosition
}; };
// Tipos de control de teclado // Tipos de control de teclado
enum class ControlScheme enum class ControlScheme {
{
CURSOR, CURSOR,
OPQA, OPQA,
WASD WASD
@@ -75,7 +73,7 @@ constexpr int DEFAULT_MUSIC_VOLUME = 80;
constexpr bool DEFAULT_MUSIC_ENABLED = true; // Musica habilitada por defecto constexpr bool DEFAULT_MUSIC_ENABLED = true; // Musica habilitada por defecto
constexpr int DEFAULT_AUDIO_VOLUME = 100; // Volumen por defecto constexpr int DEFAULT_AUDIO_VOLUME = 100; // Volumen por defecto
constexpr bool DEFAULT_AUDIO_ENABLED = true; // Audio por defecto constexpr bool DEFAULT_AUDIO_ENABLED = true; // Audio por defecto
constexpr const char *DEFAULT_PALETTE = "zx-spectrum"; // Paleta por defecto constexpr const char* DEFAULT_PALETTE = "zx-spectrum"; // Paleta por defecto
constexpr Section DEFAULT_SECTION = Section::LOGO; // Sección por defecto constexpr Section DEFAULT_SECTION = Section::LOGO; // Sección por defecto
constexpr Subsection DEFAULT_SUBSECTION = Subsection::LOGO_TO_INTRO; // Subsección por defecto constexpr Subsection DEFAULT_SUBSECTION = Subsection::LOGO_TO_INTRO; // Subsección por defecto
constexpr ControlScheme DEFAULT_CONTROL_SCHEME = ControlScheme::CURSOR; // Control por defecto constexpr ControlScheme DEFAULT_CONTROL_SCHEME = ControlScheme::CURSOR; // Control por defecto
@@ -83,11 +81,10 @@ constexpr NotificationPosition DEFAULT_NOTIFICATION_POSITION = NotificationPosit
constexpr bool DEFAULT_NOTIFICATION_SOUND = true; // Sonido de las notificaciones por defecto constexpr bool DEFAULT_NOTIFICATION_SOUND = true; // Sonido de las notificaciones por defecto
const Uint8 DEFAULT_NOTIFICATION_COLOR = static_cast<Uint8>(PaletteColor::BLUE); // Color de las notificaciones por defecto const Uint8 DEFAULT_NOTIFICATION_COLOR = static_cast<Uint8>(PaletteColor::BLUE); // Color de las notificaciones por defecto
constexpr bool DEFAULT_CONSOLE = false; // Consola desactivada por defecto constexpr bool DEFAULT_CONSOLE = false; // Consola desactivada por defecto
constexpr const char *DEFAULT_VERSION = "1.10"; // Versión por defecto constexpr const char* DEFAULT_VERSION = "1.10"; // Versión por defecto
// Estructura para las opciones de las notificaciones // Estructura para las opciones de las notificaciones
struct OptionsNotification struct OptionsNotification {
{
NotificationPosition pos; // Ubicación de las notificaciones en pantalla NotificationPosition pos; // Ubicación de las notificaciones en pantalla
bool sound; // Indica si las notificaciones suenan bool sound; // Indica si las notificaciones suenan
Uint8 color; // Color de las notificaciones Uint8 color; // Color de las notificaciones
@@ -105,10 +102,8 @@ struct OptionsNotification
color(c) {} color(c) {}
// Método que devuelve la posición horizontal // Método que devuelve la posición horizontal
NotificationPosition getHorizontalPosition() const NotificationPosition getHorizontalPosition() const {
{ switch (pos) {
switch (pos)
{
case NotificationPosition::UPPER_LEFT: case NotificationPosition::UPPER_LEFT:
case NotificationPosition::BOTTOM_LEFT: case NotificationPosition::BOTTOM_LEFT:
return NotificationPosition::LEFT; return NotificationPosition::LEFT;
@@ -125,10 +120,8 @@ struct OptionsNotification
} }
// Método que devuelve la posición vertical // Método que devuelve la posición vertical
NotificationPosition getVerticalPosition() const NotificationPosition getVerticalPosition() const {
{ switch (pos) {
switch (pos)
{
case NotificationPosition::UPPER_LEFT: case NotificationPosition::UPPER_LEFT:
case NotificationPosition::UPPER_CENTER: case NotificationPosition::UPPER_CENTER:
case NotificationPosition::UPPER_RIGHT: case NotificationPosition::UPPER_RIGHT:
@@ -145,8 +138,7 @@ struct OptionsNotification
}; };
// Estructura para saber la seccion y subseccion del programa // Estructura para saber la seccion y subseccion del programa
struct SectionState struct SectionState {
{
Section section; Section section;
Subsection subsection; Subsection subsection;
@@ -162,10 +154,8 @@ struct SectionState
}; };
// Estructura para albergar trucos // Estructura para albergar trucos
struct Cheat struct Cheat {
{ enum class CheatState : bool {
enum class CheatState : bool
{
DISABLED = false, DISABLED = false,
ENABLED = true ENABLED = true
}; };
@@ -190,8 +180,7 @@ struct Cheat
alternate_skin(as) {} alternate_skin(as) {}
// Método para comprobar si alguno de los tres primeros trucos está activo // Método para comprobar si alguno de los tres primeros trucos está activo
bool enabled() const bool enabled() const {
{
return infinite_lives == CheatState::ENABLED || return infinite_lives == CheatState::ENABLED ||
invincible == CheatState::ENABLED || invincible == CheatState::ENABLED ||
jail_is_open == CheatState::ENABLED; jail_is_open == CheatState::ENABLED;
@@ -199,8 +188,7 @@ struct Cheat
}; };
// Estructura para almacenar estadísticas // Estructura para almacenar estadísticas
struct OptionsStats struct OptionsStats {
{
int rooms; // Cantidad de habitaciones visitadas int rooms; // Cantidad de habitaciones visitadas
int items; // Cantidad de items obtenidos int items; // Cantidad de items obtenidos
std::string worst_nightmare; // Habitación con más muertes acumuladas std::string worst_nightmare; // Habitación con más muertes acumuladas
@@ -219,8 +207,7 @@ struct OptionsStats
}; };
// Estructura con opciones de la ventana // Estructura con opciones de la ventana
struct OptionsWindow struct OptionsWindow {
{
int zoom; // Zoom de la ventana int zoom; // Zoom de la ventana
int max_zoom; // Máximo tamaño de zoom para la ventana int max_zoom; // Máximo tamaño de zoom para la ventana
@@ -236,8 +223,7 @@ struct OptionsWindow
}; };
// Estructura para gestionar el borde de la pantalla // Estructura para gestionar el borde de la pantalla
struct Border struct Border {
{
bool enabled; // Indica si se ha de mostrar el borde bool enabled; // Indica si se ha de mostrar el borde
int width; // Ancho del borde int width; // Ancho del borde
int height; // Alto del borde int height; // Alto del borde
@@ -256,8 +242,7 @@ struct Border
}; };
// Estructura para las opciones de video // Estructura para las opciones de video
struct OptionsVideo struct OptionsVideo {
{
Uint32 mode; // Contiene el valor del modo de pantalla completa Uint32 mode; // Contiene el valor del modo de pantalla completa
ScreenFilter filter; // Filtro usado para el escalado de la imagen ScreenFilter filter; // Filtro usado para el escalado de la imagen
bool vertical_sync; // Indica si se quiere usar vsync o no bool vertical_sync; // Indica si se quiere usar vsync o no
@@ -279,7 +264,7 @@ struct OptionsVideo
palette(DEFAULT_PALETTE) {} palette(DEFAULT_PALETTE) {}
// Constructor // Constructor
OptionsVideo(Uint32 m, ScreenFilter f, bool vs, bool s, bool is, bool ka, Border b, const std::string &p) OptionsVideo(Uint32 m, ScreenFilter f, bool vs, bool s, bool is, bool ka, Border b, const std::string& p)
: mode(m), : mode(m),
filter(f), filter(f),
vertical_sync(vs), vertical_sync(vs),
@@ -291,8 +276,7 @@ struct OptionsVideo
}; };
// Estructura para las opciones de musica // Estructura para las opciones de musica
struct OptionsMusic struct OptionsMusic {
{
bool enabled; // Indica si la música suena o no bool enabled; // Indica si la música suena o no
int volume; // Volumen al que suena la música (0 a 128 internamente) int volume; // Volumen al que suena la música (0 a 128 internamente)
@@ -307,22 +291,19 @@ struct OptionsMusic
volume(convertVolume(v)) {} // Convierte el volumen usando el método estático volume(convertVolume(v)) {} // Convierte el volumen usando el método estático
// Método para establecer el volumen // Método para establecer el volumen
void setVolume(int v) void setVolume(int v) {
{
v = std::clamp(v, 0, 100); // Ajusta v al rango [0, 100] v = std::clamp(v, 0, 100); // Ajusta v al rango [0, 100]
volume = convertVolume(v); // Convierte al rango interno volume = convertVolume(v); // Convierte al rango interno
} }
// Método estático para convertir de 0-100 a 0-128 // Método estático para convertir de 0-100 a 0-128
static int convertVolume(int v) static int convertVolume(int v) {
{
return (v * 128) / 100; return (v * 128) / 100;
} }
}; };
// Estructura para las opciones de sonido // Estructura para las opciones de sonido
struct OptionsSound struct OptionsSound {
{
bool enabled; // Indica si los sonidos suenan o no bool enabled; // Indica si los sonidos suenan o no
int volume; // Volumen al que suenan los sonidos (0 a 128 internamente) int volume; // Volumen al que suenan los sonidos (0 a 128 internamente)
@@ -337,22 +318,19 @@ struct OptionsSound
volume(convertVolume(v)) {} // También lo integra aquí volume(convertVolume(v)) {} // También lo integra aquí
// Método para establecer el volumen // Método para establecer el volumen
void setVolume(int v) void setVolume(int v) {
{
v = std::clamp(v, 0, 100); // Ajusta v al rango [0, 100] v = std::clamp(v, 0, 100); // Ajusta v al rango [0, 100]
volume = convertVolume(v); // Convierte al rango interno volume = convertVolume(v); // Convierte al rango interno
} }
// Método estático para convertir de 0-100 a 0-128 // Método estático para convertir de 0-100 a 0-128
static int convertVolume(int v) static int convertVolume(int v) {
{
return (v * 128) / 100; return (v * 128) / 100;
} }
}; };
// Estructura para las opciones de audio // Estructura para las opciones de audio
struct OptionsAudio struct OptionsAudio {
{
OptionsMusic music; // Opciones para la música OptionsMusic music; // Opciones para la música
OptionsSound sound; // Opciones para los efectos de sonido OptionsSound sound; // Opciones para los efectos de sonido
bool enabled; // Indica si el audio está activo o no bool enabled; // Indica si el audio está activo o no
@@ -374,8 +352,7 @@ struct OptionsAudio
}; };
// Estructura para las opciones de juego // Estructura para las opciones de juego
struct OptionsGame struct OptionsGame {
{
int width; // Ancho de la resolucion del juego int width; // Ancho de la resolucion del juego
int height; // Alto de la resolucion del juego int height; // Alto de la resolucion del juego
@@ -391,8 +368,7 @@ struct OptionsGame
}; };
// Estructura con todas las opciones de configuración del programa // Estructura con todas las opciones de configuración del programa
struct Options struct Options {
{
std::string version; // Versión del fichero de configuración. Sirve para saber si las opciones son compatibles std::string version; // Versión del fichero de configuración. Sirve para saber si las opciones son compatibles
bool console; // Indica si ha de mostrar información por la consola de texto bool console; // Indica si ha de mostrar información por la consola de texto
Cheat cheats; // Contiene trucos y ventajas para el juego Cheat cheats; // Contiene trucos y ventajas para el juego
@@ -440,7 +416,7 @@ extern Options options;
void initOptions(); void initOptions();
// Carga las opciones desde un fichero // Carga las opciones desde un fichero
bool loadOptionsFromFile(const std::string &file_path); bool loadOptionsFromFile(const std::string& file_path);
// Guarda las opciones a un fichero // Guarda las opciones a un fichero
bool saveOptionsToFile(const std::string &file_path); bool saveOptionsToFile(const std::string& file_path);

View File

@@ -1,26 +1,26 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect, SDL_Point #include <SDL3/SDL_rect.h> // Para SDL_Rect, SDL_Point
#include <SDL2/SDL_render.h> // Para SDL_RendererFlip, SDL_FLIP_NONE #include <SDL3/SDL_render.h> // Para SDL_RendererFlip, SDL_FLIP_NONE
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr, __shared_ptr_access #include <memory> // Para shared_ptr, __shared_ptr_access
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
#include "defines.h" // Para BORDER_TOP, BLOCK #include "defines.h" // Para BORDER_TOP, BLOCK
#include "room.h"
#include "s_animated_sprite.h" // Para SAnimatedSprite #include "s_animated_sprite.h" // Para SAnimatedSprite
#include "utils.h" // Para Color #include "utils.h" // Para Color
#include "room.h"
struct JA_Sound_t; // lines 13-13 struct JA_Sound_t; // lines 13-13
enum class PlayerState enum class PlayerState {
{
STANDING, STANDING,
JUMPING, JUMPING,
FALLING, FALLING,
}; };
struct PlayerSpawn struct PlayerSpawn {
{
float x; float x;
float y; float y;
float vx; float vx;
@@ -30,15 +30,27 @@ struct PlayerSpawn
SDL_RendererFlip flip; SDL_RendererFlip flip;
// Constructor por defecto // Constructor por defecto
PlayerSpawn() : x(0), y(0), vx(0), vy(0), jump_init_pos(0), state(PlayerState::STANDING), flip(SDL_FLIP_NONE) {} PlayerSpawn()
: x(0),
y(0),
vx(0),
vy(0),
jump_init_pos(0),
state(PlayerState::STANDING),
flip(SDL_FLIP_NONE) {}
// Constructor // Constructor
PlayerSpawn(float x, float y, float vx, float vy, int jump_init_pos, PlayerState state, SDL_RendererFlip flip) PlayerSpawn(float x, float y, float vx, float vy, int jump_init_pos, PlayerState state, SDL_RendererFlip flip)
: x(x), y(y), vx(vx), vy(vy), jump_init_pos(jump_init_pos), state(state), flip(flip) {} : x(x),
y(y),
vx(vx),
vy(vy),
jump_init_pos(jump_init_pos),
state(state),
flip(flip) {}
}; };
struct PlayerData struct PlayerData {
{
PlayerSpawn spawn; PlayerSpawn spawn;
std::string texture_path; std::string texture_path;
std::string animations_path; std::string animations_path;
@@ -46,12 +58,14 @@ struct PlayerData
// Constructor // Constructor
PlayerData(PlayerSpawn spawn, std::string texture_path, std::string animations_path, std::shared_ptr<Room> room) PlayerData(PlayerSpawn spawn, std::string texture_path, std::string animations_path, std::shared_ptr<Room> room)
: spawn(spawn), texture_path(texture_path), animations_path(animations_path), room(room) {} : spawn(spawn),
texture_path(texture_path),
animations_path(animations_path),
room(room) {}
}; };
class Player class Player {
{ public:
public:
// Constantes // Constantes
static constexpr int WIDTH_ = 8; // Ancho del jugador static constexpr int WIDTH_ = 8; // Ancho del jugador
static constexpr int HEIGHT_ = 16; // ALto del jugador static constexpr int HEIGHT_ = 16; // ALto del jugador
@@ -81,8 +95,8 @@ public:
RoomBorder border_ = RoomBorder::TOP; // Indica en cual de los cuatro bordes se encuentra RoomBorder border_ = RoomBorder::TOP; // Indica en cual de los cuatro bordes se encuentra
SDL_Rect last_position_; // Contiene la ultima posición del jugador, por si hay que deshacer algun movimiento SDL_Rect last_position_; // Contiene la ultima posición del jugador, por si hay que deshacer algun movimiento
int jump_init_pos_; // Valor del eje Y en el que se inicia el salto int jump_init_pos_; // Valor del eje Y en el que se inicia el salto
std::vector<JA_Sound_t *> jumping_sound_; // Vecor con todos los sonidos del salto std::vector<JA_Sound_t*> jumping_sound_; // Vecor con todos los sonidos del salto
std::vector<JA_Sound_t *> falling_sound_; // Vecor con todos los sonidos de la caída std::vector<JA_Sound_t*> falling_sound_; // Vecor con todos los sonidos de la caída
int jumping_counter_ = 0; // Cuenta el tiempo de salto int jumping_counter_ = 0; // Cuenta el tiempo de salto
int falling_counter_ = 0; // Cuenta el tiempo de caida int falling_counter_ = 0; // Cuenta el tiempo de caida
@@ -148,19 +162,19 @@ public:
void placeSprite() { sprite_->setPos(x_, y_); } void placeSprite() { sprite_->setPos(x_, y_); }
// Aplica los valores de spawn al jugador // Aplica los valores de spawn al jugador
void applySpawnValues(const PlayerSpawn &spawn); void applySpawnValues(const PlayerSpawn& spawn);
// Inicializa el sprite del jugador // Inicializa el sprite del jugador
void initSprite(const std::string &texture_path, const std::string &animations_path); void initSprite(const std::string& texture_path, const std::string& animations_path);
#ifdef DEBUG #ifdef DEBUG
// Pinta la información de debug del jugador // Pinta la información de debug del jugador
void renderDebugInfo(); void renderDebugInfo();
#endif #endif
public: public:
// Constructor // Constructor
explicit Player(const PlayerData &player); explicit Player(const PlayerData& player);
// Destructor // Destructor
~Player() = default; ~Player() = default;
@@ -184,7 +198,7 @@ public:
SDL_Rect getRect() { return {static_cast<int>(x_), static_cast<int>(y_), WIDTH_, HEIGHT_}; } SDL_Rect getRect() { return {static_cast<int>(x_), static_cast<int>(y_), WIDTH_, HEIGHT_}; }
// Obtiene el rectangulo de colision del jugador // Obtiene el rectangulo de colision del jugador
SDL_Rect &getCollider() { return collider_box_; } SDL_Rect& getCollider() { return collider_box_; }
// Obtiene el estado de reaparición del jugador // Obtiene el estado de reaparición del jugador
PlayerSpawn getSpawnParams() { return {x_, y_, vx_, vy_, jump_init_pos_, state_, sprite_->getFlip()}; } PlayerSpawn getSpawnParams() { return {x_, y_, vx_, vy_, jump_init_pos_, state_, sprite_->getFlip()}; }

View File

@@ -1,12 +1,15 @@
#include "resource.h" #include "resource.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event, SDL_KEYDOWN
#include <SDL2/SDL_keycode.h> // Para SDLK_ESCAPE #include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event, SDL_KEYDOWN
#include <SDL2/SDL_rect.h> // Para SDL_Rect #include <SDL3/SDL_keycode.h> // Para SDLK_ESCAPE
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_stdinc.h> // Para Uint8
#include <stdlib.h> // Para exit, size_t #include <stdlib.h> // Para exit, size_t
#include <algorithm> // Para find_if #include <algorithm> // Para find_if
#include <iostream> // Para basic_ostream, operator<<, endl, cout #include <iostream> // Para basic_ostream, operator<<, endl, cout
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include "asset.h" // Para AssetType, Asset #include "asset.h" // Para AssetType, Asset
#include "jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_Loa... #include "jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_Loa...
#include "options.h" // Para Options, OptionsGame, options #include "options.h" // Para Options, OptionsGame, options
@@ -18,7 +21,7 @@ struct JA_Music_t; // lines 17-17
struct JA_Sound_t; // lines 18-18 struct JA_Sound_t; // lines 18-18
// [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado // [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
Resource *Resource::resource_ = nullptr; Resource* Resource::resource_ = nullptr;
// [SINGLETON] Crearemos el objeto screen con esta función estática // [SINGLETON] Crearemos el objeto screen con esta función estática
void Resource::init() { Resource::resource_ = new Resource(); } void Resource::init() { Resource::resource_ = new Resource(); }
@@ -27,14 +30,13 @@ void Resource::init() { Resource::resource_ = new Resource(); }
void Resource::destroy() { delete Resource::resource_; } void Resource::destroy() { delete Resource::resource_; }
// [SINGLETON] Con este método obtenemos el objeto screen y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto screen y podemos trabajar con él
Resource *Resource::get() { return Resource::resource_; } Resource* Resource::get() { return Resource::resource_; }
// Constructor // Constructor
Resource::Resource() { load(); } Resource::Resource() { load(); }
// Vacia todos los vectores de recursos // Vacia todos los vectores de recursos
void Resource::clear() void Resource::clear() {
{
clearSounds(); clearSounds();
clearMusics(); clearMusics();
surfaces_.clear(); surfaces_.clear();
@@ -45,8 +47,7 @@ void Resource::clear()
} }
// Carga todos los recursos // Carga todos los recursos
void Resource::load() void Resource::load() {
{
calculateTotal(); calculateTotal();
Screen::get()->show(); Screen::get()->show();
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
@@ -64,20 +65,16 @@ void Resource::load()
} }
// Recarga todos los recursos // Recarga todos los recursos
void Resource::reload() void Resource::reload() {
{
clear(); clear();
load(); load();
} }
// Obtiene el sonido a partir de un nombre // Obtiene el sonido a partir de un nombre
JA_Sound_t *Resource::getSound(const std::string &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; });
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; return it->sound;
} }
@@ -86,13 +83,10 @@ JA_Sound_t *Resource::getSound(const std::string &name)
} }
// Obtiene la música a partir de un nombre // Obtiene la música a partir de un nombre
JA_Music_t *Resource::getMusic(const std::string &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; });
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; return it->music;
} }
@@ -101,13 +95,10 @@ JA_Music_t *Resource::getMusic(const std::string &name)
} }
// Obtiene la surface a partir de un nombre // Obtiene la surface a partir de un nombre
std::shared_ptr<Surface> Resource::getSurface(const std::string &name) std::shared_ptr<Surface> Resource::getSurface(const std::string& name) {
{ auto it = std::find_if(surfaces_.begin(), surfaces_.end(), [&name](const auto& t) { return t.name == name; });
auto it = std::find_if(surfaces_.begin(), surfaces_.end(), [&name](const auto &t)
{ return t.name == name; });
if (it != surfaces_.end()) if (it != surfaces_.end()) {
{
return it->surface; return it->surface;
} }
@@ -116,13 +107,10 @@ std::shared_ptr<Surface> Resource::getSurface(const std::string &name)
} }
// Obtiene la paleta a partir de un nombre // Obtiene la paleta a partir de un nombre
Palette Resource::getPalette(const std::string &name) Palette Resource::getPalette(const std::string& name) {
{ auto it = std::find_if(palettes_.begin(), palettes_.end(), [&name](const auto& t) { return t.name == name; });
auto it = std::find_if(palettes_.begin(), palettes_.end(), [&name](const auto &t)
{ return t.name == name; });
if (it != palettes_.end()) if (it != palettes_.end()) {
{
return it->palette; return it->palette;
} }
@@ -131,13 +119,10 @@ Palette Resource::getPalette(const std::string &name)
} }
// Obtiene el fichero de texto a partir de un nombre // Obtiene el fichero de texto a partir de un nombre
std::shared_ptr<TextFile> Resource::getTextFile(const std::string &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; });
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; return it->text_file;
} }
@@ -146,13 +131,10 @@ std::shared_ptr<TextFile> Resource::getTextFile(const std::string &name)
} }
// Obtiene el objeto de texto a partir de un nombre // Obtiene el objeto de texto a partir de un nombre
std::shared_ptr<Text> Resource::getText(const std::string &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; });
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; return it->text;
} }
@@ -161,13 +143,10 @@ std::shared_ptr<Text> Resource::getText(const std::string &name)
} }
// Obtiene la animación a partir de un nombre // Obtiene la animación a partir de un nombre
Animations &Resource::getAnimations(const std::string &name) Animations& Resource::getAnimations(const std::string& name) {
{ auto it = std::find_if(animations_.begin(), animations_.end(), [&name](const auto& a) { return a.name == 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; return it->animation;
} }
@@ -176,13 +155,10 @@ Animations &Resource::getAnimations(const std::string &name)
} }
// Obtiene el mapa de tiles a partir de un nombre // Obtiene el mapa de tiles a partir de un nombre
std::vector<int> &Resource::getTileMap(const std::string &name) std::vector<int>& Resource::getTileMap(const std::string& name) {
{ auto it = std::find_if(tile_maps_.begin(), tile_maps_.end(), [&name](const auto& t) { return t.name == name; });
auto it = std::find_if(tile_maps_.begin(), tile_maps_.end(), [&name](const auto &t)
{ return t.name == name; });
if (it != tile_maps_.end()) if (it != tile_maps_.end()) {
{
return it->tileMap; return it->tileMap;
} }
@@ -191,13 +167,10 @@ std::vector<int> &Resource::getTileMap(const std::string &name)
} }
// Obtiene la habitación a partir de un nombre // Obtiene la habitación a partir de un nombre
std::shared_ptr<RoomData> Resource::getRoom(const std::string &name) std::shared_ptr<RoomData> Resource::getRoom(const std::string& name) {
{ auto it = std::find_if(rooms_.begin(), rooms_.end(), [&name](const auto& r) { return r.name == name; });
auto it = std::find_if(rooms_.begin(), rooms_.end(), [&name](const auto &r)
{ return r.name == name; });
if (it != rooms_.end()) if (it != rooms_.end()) {
{
return it->room; return it->room;
} }
@@ -206,20 +179,17 @@ std::shared_ptr<RoomData> Resource::getRoom(const std::string &name)
} }
// Obtiene todas las habitaciones // Obtiene todas las habitaciones
std::vector<ResourceRoom> &Resource::getRooms() std::vector<ResourceRoom>& Resource::getRooms() {
{
return rooms_; return rooms_;
} }
// Carga los sonidos // Carga los sonidos
void Resource::loadSounds() void Resource::loadSounds() {
{
std::cout << "\n>> SOUND FILES" << std::endl; std::cout << "\n>> SOUND FILES" << std::endl;
auto list = Asset::get()->getListByType(AssetType::SOUND); auto list = Asset::get()->getListByType(AssetType::SOUND);
sounds_.clear(); sounds_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
sounds_.emplace_back(ResourceSound(name, JA_LoadSound(l.c_str()))); sounds_.emplace_back(ResourceSound(name, JA_LoadSound(l.c_str())));
printWithDots("Sound : ", name, "[ LOADED ]"); printWithDots("Sound : ", name, "[ LOADED ]");
@@ -228,14 +198,12 @@ void Resource::loadSounds()
} }
// Carga las musicas // Carga las musicas
void Resource::loadMusics() void Resource::loadMusics() {
{
std::cout << "\n>> MUSIC FILES" << std::endl; std::cout << "\n>> MUSIC FILES" << std::endl;
auto list = Asset::get()->getListByType(AssetType::MUSIC); auto list = Asset::get()->getListByType(AssetType::MUSIC);
musics_.clear(); musics_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
musics_.emplace_back(ResourceMusic(name, JA_LoadMusic(l.c_str()))); musics_.emplace_back(ResourceMusic(name, JA_LoadMusic(l.c_str())));
printWithDots("Music : ", name, "[ LOADED ]"); printWithDots("Music : ", name, "[ LOADED ]");
@@ -244,14 +212,12 @@ void Resource::loadMusics()
} }
// Carga las texturas // Carga las texturas
void Resource::loadSurfaces() void Resource::loadSurfaces() {
{
std::cout << "\n>> SURFACES" << std::endl; std::cout << "\n>> SURFACES" << std::endl;
auto list = Asset::get()->getListByType(AssetType::BITMAP); auto list = Asset::get()->getListByType(AssetType::BITMAP);
surfaces_.clear(); surfaces_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
surfaces_.emplace_back(ResourceSurface(name, std::make_shared<Surface>(l))); surfaces_.emplace_back(ResourceSurface(name, std::make_shared<Surface>(l)));
surfaces_.back().surface->setTransparentColor(0); surfaces_.back().surface->setTransparentColor(0);
@@ -269,14 +235,12 @@ void Resource::loadSurfaces()
} }
// Carga las paletas // Carga las paletas
void Resource::loadPalettes() void Resource::loadPalettes() {
{
std::cout << "\n>> PALETTES" << std::endl; std::cout << "\n>> PALETTES" << std::endl;
auto list = Asset::get()->getListByType(AssetType::PALETTE); auto list = Asset::get()->getListByType(AssetType::PALETTE);
palettes_.clear(); palettes_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
palettes_.emplace_back(ResourcePalette(name, readPalFile(l))); palettes_.emplace_back(ResourcePalette(name, readPalFile(l)));
updateLoadingProgress(); updateLoadingProgress();
@@ -284,14 +248,12 @@ void Resource::loadPalettes()
} }
// Carga los ficheros de texto // Carga los ficheros de texto
void Resource::loadTextFiles() void Resource::loadTextFiles() {
{
std::cout << "\n>> TEXT FILES" << std::endl; std::cout << "\n>> TEXT FILES" << std::endl;
auto list = Asset::get()->getListByType(AssetType::FONT); auto list = Asset::get()->getListByType(AssetType::FONT);
text_files_.clear(); text_files_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
text_files_.emplace_back(ResourceTextFile(name, loadTextFile(l))); text_files_.emplace_back(ResourceTextFile(name, loadTextFile(l)));
updateLoadingProgress(); updateLoadingProgress();
@@ -299,14 +261,12 @@ void Resource::loadTextFiles()
} }
// Carga las animaciones // Carga las animaciones
void Resource::loadAnimations() void Resource::loadAnimations() {
{
std::cout << "\n>> ANIMATIONS" << std::endl; std::cout << "\n>> ANIMATIONS" << std::endl;
auto list = Asset::get()->getListByType(AssetType::ANIMATION); auto list = Asset::get()->getListByType(AssetType::ANIMATION);
animations_.clear(); animations_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
animations_.emplace_back(ResourceAnimation(name, loadAnimationsFromFile(l))); animations_.emplace_back(ResourceAnimation(name, loadAnimationsFromFile(l)));
updateLoadingProgress(); updateLoadingProgress();
@@ -314,14 +274,12 @@ void Resource::loadAnimations()
} }
// Carga los mapas de tiles // Carga los mapas de tiles
void Resource::loadTileMaps() void Resource::loadTileMaps() {
{
std::cout << "\n>> TILE MAPS" << std::endl; std::cout << "\n>> TILE MAPS" << std::endl;
auto list = Asset::get()->getListByType(AssetType::TILEMAP); auto list = Asset::get()->getListByType(AssetType::TILEMAP);
tile_maps_.clear(); tile_maps_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
tile_maps_.emplace_back(ResourceTileMap(name, loadRoomTileFile(l))); tile_maps_.emplace_back(ResourceTileMap(name, loadRoomTileFile(l)));
printWithDots("TileMap : ", name, "[ LOADED ]"); printWithDots("TileMap : ", name, "[ LOADED ]");
@@ -330,14 +288,12 @@ void Resource::loadTileMaps()
} }
// Carga las habitaciones // Carga las habitaciones
void Resource::loadRooms() void Resource::loadRooms() {
{
std::cout << "\n>> ROOMS" << std::endl; std::cout << "\n>> ROOMS" << std::endl;
auto list = Asset::get()->getListByType(AssetType::ROOM); auto list = Asset::get()->getListByType(AssetType::ROOM);
rooms_.clear(); rooms_.clear();
for (const auto &l : list) for (const auto& l : list) {
{
auto name = getFileName(l); auto name = getFileName(l);
rooms_.emplace_back(ResourceRoom(name, std::make_shared<RoomData>(loadRoomFile(l)))); rooms_.emplace_back(ResourceRoom(name, std::make_shared<RoomData>(loadRoomFile(l))));
printWithDots("Room : ", name, "[ LOADED ]"); printWithDots("Room : ", name, "[ LOADED ]");
@@ -345,17 +301,17 @@ void Resource::loadRooms()
} }
} }
void Resource::createText() void Resource::createText() {
{ struct ResourceInfo {
struct ResourceInfo
{
std::string key; // Identificador del recurso std::string key; // Identificador del recurso
std::string textureFile; // Nombre del archivo de textura std::string textureFile; // Nombre del archivo de textura
std::string textFile; // Nombre del archivo de texto std::string textFile; // Nombre del archivo de texto
// Constructor para facilitar la creación de objetos ResourceInfo // Constructor para facilitar la creación de objetos ResourceInfo
ResourceInfo(const std::string &k, const std::string &tFile, const std::string &txtFile) ResourceInfo(const std::string& k, const std::string& tFile, const std::string& txtFile)
: key(k), textureFile(tFile), textFile(txtFile) {} : key(k),
textureFile(tFile),
textFile(txtFile) {}
}; };
std::cout << "\n>> CREATING TEXT_OBJECTS" << std::endl; std::cout << "\n>> CREATING TEXT_OBJECTS" << std::endl;
@@ -367,23 +323,17 @@ void Resource::createText()
{"subatomic", "subatomic.gif", "subatomic.txt"}, {"subatomic", "subatomic.gif", "subatomic.txt"},
{"8bithud", "8bithud.gif", "8bithud.txt"}}; {"8bithud", "8bithud.gif", "8bithud.txt"}};
for (const auto &resource : resources) for (const auto& resource : resources) {
{ texts_.emplace_back(ResourceText(resource.key, std::make_shared<Text>(getSurface(resource.textureFile), getTextFile(resource.textFile))));
texts_.emplace_back(ResourceText(resource.key, std::make_shared<Text>(
getSurface(resource.textureFile),
getTextFile(resource.textFile))));
printWithDots("Text : ", resource.key, "[ DONE ]"); printWithDots("Text : ", resource.key, "[ DONE ]");
} }
} }
// Vacía el vector de sonidos // Vacía el vector de sonidos
void Resource::clearSounds() void Resource::clearSounds() {
{
// Itera sobre el vector y libera los recursos asociados a cada JA_Sound_t // Itera sobre el vector y libera los recursos asociados a cada JA_Sound_t
for (auto &sound : sounds_) for (auto& sound : sounds_) {
{ if (sound.sound) {
if (sound.sound)
{
JA_DeleteSound(sound.sound); JA_DeleteSound(sound.sound);
sound.sound = nullptr; sound.sound = nullptr;
} }
@@ -392,13 +342,10 @@ void Resource::clearSounds()
} }
// Vacía el vector de musicas // Vacía el vector de musicas
void Resource::clearMusics() void Resource::clearMusics() {
{
// Itera sobre el vector y libera los recursos asociados a cada JA_Music_t // Itera sobre el vector y libera los recursos asociados a cada JA_Music_t
for (auto &music : musics_) for (auto& music : musics_) {
{ if (music.music) {
if (music.music)
{
JA_DeleteMusic(music.music); JA_DeleteMusic(music.music);
music.music = nullptr; music.music = nullptr;
} }
@@ -407,8 +354,7 @@ void Resource::clearMusics()
} }
// Calcula el numero de recursos para cargar // Calcula el numero de recursos para cargar
void Resource::calculateTotal() void Resource::calculateTotal() {
{
std::vector<AssetType> assetTypes = { std::vector<AssetType> assetTypes = {
AssetType::SOUND, AssetType::SOUND,
AssetType::MUSIC, AssetType::MUSIC,
@@ -420,8 +366,7 @@ void Resource::calculateTotal()
AssetType::ROOM}; AssetType::ROOM};
size_t total = 0; size_t total = 0;
for (const auto &assetType : assetTypes) for (const auto& assetType : assetTypes) {
{
auto list = Asset::get()->getListByType(assetType); auto list = Asset::get()->getListByType(assetType);
total += list.size(); total += list.size();
} }
@@ -430,8 +375,7 @@ void Resource::calculateTotal()
} }
// Muestra el progreso de carga // Muestra el progreso de carga
void Resource::renderProgress() void Resource::renderProgress() {
{
constexpr int X_PADDING = 10; constexpr int X_PADDING = 10;
constexpr int Y_PADDING = 10; constexpr int Y_PADDING = 10;
constexpr int BAR_HEIGHT = 10; constexpr int BAR_HEIGHT = 10;
@@ -452,19 +396,15 @@ void Resource::renderProgress()
} }
// Comprueba los eventos de la pantalla de carga // Comprueba los eventos de la pantalla de carga
void Resource::checkEvents() void Resource::checkEvents() {
{
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event)) {
{ switch (event.type) {
switch (event.type)
{
case SDL_QUIT: case SDL_QUIT:
exit(0); exit(0);
break; break;
case SDL_KEYDOWN: case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) if (event.key.keysym.sym == SDLK_ESCAPE) {
{
exit(0); exit(0);
} }
break; break;
@@ -473,11 +413,9 @@ void Resource::checkEvents()
} }
// Actualiza el progreso de carga // Actualiza el progreso de carga
void Resource::updateLoadingProgress(int steps) void Resource::updateLoadingProgress(int steps) {
{
count_.add(1); count_.add(1);
if (count_.loaded % steps == 0 || count_.loaded == count_.total) if (count_.loaded % steps == 0 || count_.loaded == count_.total) {
{
renderProgress(); renderProgress();
} }
checkEvents(); checkEvents();

View File

@@ -1,10 +1,12 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect, SDL_Point #include <SDL3/SDL_rect.h> // Para SDL_Rect, SDL_Point
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
#include "enemy.h" // Para EnemyData #include "enemy.h" // Para EnemyData
#include "item.h" // Para ItemData #include "item.h" // Para ItemData
#include "utils.h" // Para LineHorizontal, LineDiagonal, LineVertical #include "utils.h" // Para LineHorizontal, LineDiagonal, LineVertical
@@ -12,8 +14,7 @@ class SSprite; // lines 12-12
class Surface; // lines 13-13 class Surface; // lines 13-13
struct ScoreboardData; // lines 15-15 struct ScoreboardData; // lines 15-15
enum class TileType enum class TileType {
{
EMPTY, EMPTY,
WALL, WALL,
PASSABLE, PASSABLE,
@@ -23,23 +24,19 @@ enum class TileType
ANIMATED ANIMATED
}; };
enum class RoomBorder : int enum class RoomBorder : int {
{
TOP = 0, TOP = 0,
RIGHT = 1, RIGHT = 1,
BOTTOM = 2, BOTTOM = 2,
LEFT = 3 LEFT = 3
}; };
struct AnimatedTile {
struct AnimatedTile
{
std::shared_ptr<SSprite> sprite; // SSprite para dibujar el tile std::shared_ptr<SSprite> sprite; // SSprite para dibujar el tile
int x_orig; // Poicion X donde se encuentra el primer tile de la animacion en la tilesheet int x_orig; // Poicion X donde se encuentra el primer tile de la animacion en la tilesheet
}; };
struct RoomData struct RoomData {
{
std::string number; // Numero de la habitación std::string number; // Numero de la habitación
std::string name; // Nombre de la habitación std::string name; // Nombre de la habitación
std::string bg_color; // Color de fondo de la habitación std::string bg_color; // Color de fondo de la habitación
@@ -59,23 +56,22 @@ struct RoomData
}; };
// Carga las variables desde un fichero de mapa // Carga las variables desde un fichero de mapa
RoomData loadRoomFile(const std::string &file_path, bool verbose = false); RoomData loadRoomFile(const std::string& file_path, bool verbose = false);
// Carga las variables y texturas desde un fichero de mapa de tiles // Carga las variables y texturas desde un fichero de mapa de tiles
std::vector<int> loadRoomTileFile(const std::string &file_path, bool verbose = false); std::vector<int> loadRoomTileFile(const std::string& file_path, bool verbose = false);
// Asigna variables a una estructura RoomData // Asigna variables a una estructura RoomData
bool setRoom(RoomData *room, const std::string &key, const std::string &value); bool setRoom(RoomData* room, const std::string& key, const std::string& value);
// Asigna variables a una estructura EnemyData // Asigna variables a una estructura EnemyData
bool setEnemy(EnemyData *enemy, const std::string &key, const std::string &value); bool setEnemy(EnemyData* enemy, const std::string& key, const std::string& value);
// Asigna variables a una estructura ItemData // Asigna variables a una estructura ItemData
bool setItem(ItemData *item, const std::string &key, const std::string &value); bool setItem(ItemData* item, const std::string& key, const std::string& value);
class Room class Room {
{ private:
private:
// Constantes // Constantes
static constexpr int TILE_SIZE_ = 8; // Ancho del tile en pixels static constexpr int TILE_SIZE_ = 8; // Ancho del tile en pixels
static constexpr int MAP_WIDTH_ = 32; // Ancho del mapa en tiles static constexpr int MAP_WIDTH_ = 32; // Ancho del mapa en tiles
@@ -115,7 +111,7 @@ private:
std::vector<LineHorizontal> conveyor_belt_floors_; // Lista con las superficies automaticas de la habitación std::vector<LineHorizontal> conveyor_belt_floors_; // Lista con las superficies automaticas de la habitación
int tile_set_width_; // Ancho del tileset en tiles int tile_set_width_; // Ancho del tileset en tiles
void initializeRoom(const RoomData &room); void initializeRoom(const RoomData& room);
// Pinta el mapa de la habitación en la textura // Pinta el mapa de la habitación en la textura
void fillMapTexture(); void fillMapTexture();
@@ -159,15 +155,15 @@ private:
// Inicializa las superficies de colision // Inicializa las superficies de colision
void initRoomSurfaces(); void initRoomSurfaces();
public: public:
// Constructor // Constructor
Room(const std::string &room_path, std::shared_ptr<ScoreboardData> data); Room(const std::string& room_path, std::shared_ptr<ScoreboardData> data);
// Destructor // Destructor
~Room() = default; ~Room() = default;
// Devuelve el nombre de la habitación // Devuelve el nombre de la habitación
const std::string &getName() const { return name_; } const std::string& getName() const { return name_; }
// Devuelve el color de la habitación // Devuelve el color de la habitación
Uint8 getBGColor() const { return stringToColor(bg_color_); } Uint8 getBGColor() const { return stringToColor(bg_color_); }
@@ -194,10 +190,10 @@ public:
TileType getTile(SDL_Point point); TileType getTile(SDL_Point point);
// Indica si hay colision con un enemigo a partir de un rectangulo // Indica si hay colision con un enemigo a partir de un rectangulo
bool enemyCollision(SDL_Rect &rect); bool enemyCollision(SDL_Rect& rect);
// Indica si hay colision con un objeto a partir de un rectangulo // Indica si hay colision con un objeto a partir de un rectangulo
bool itemCollision(SDL_Rect &rect); bool itemCollision(SDL_Rect& rect);
// Obten el tamaño del tile // Obten el tamaño del tile
int getTileSize() const { return TILE_SIZE_; } int getTileSize() const { return TILE_SIZE_; }
@@ -206,37 +202,37 @@ public:
int getSlopeHeight(SDL_Point p, TileType slope); int getSlopeHeight(SDL_Point p, TileType slope);
// Comprueba las colisiones // Comprueba las colisiones
int checkRightSurfaces(SDL_Rect *rect); int checkRightSurfaces(SDL_Rect* rect);
// Comprueba las colisiones // Comprueba las colisiones
int checkLeftSurfaces(SDL_Rect *rect); int checkLeftSurfaces(SDL_Rect* rect);
// Comprueba las colisiones // Comprueba las colisiones
int checkTopSurfaces(SDL_Rect *rect); int checkTopSurfaces(SDL_Rect* rect);
// Comprueba las colisiones // Comprueba las colisiones
int checkBottomSurfaces(SDL_Rect *rect); int checkBottomSurfaces(SDL_Rect* rect);
// Comprueba las colisiones // Comprueba las colisiones
int checkAutoSurfaces(SDL_Rect *rect); int checkAutoSurfaces(SDL_Rect* rect);
// Comprueba las colisiones // Comprueba las colisiones
bool checkTopSurfaces(SDL_Point *p); bool checkTopSurfaces(SDL_Point* p);
// Comprueba las colisiones // Comprueba las colisiones
bool checkAutoSurfaces(SDL_Point *p); bool checkAutoSurfaces(SDL_Point* p);
// Comprueba las colisiones // Comprueba las colisiones
int checkLeftSlopes(const LineVertical *line); int checkLeftSlopes(const LineVertical* line);
// Comprueba las colisiones // Comprueba las colisiones
bool checkLeftSlopes(SDL_Point *p); bool checkLeftSlopes(SDL_Point* p);
// Comprueba las colisiones // Comprueba las colisiones
int checkRightSlopes(const LineVertical *line); int checkRightSlopes(const LineVertical* line);
// Comprueba las colisiones // Comprueba las colisiones
bool checkRightSlopes(SDL_Point *p); bool checkRightSlopes(SDL_Point* p);
// Pone el mapa en modo pausa // Pone el mapa en modo pausa
void setPaused(bool value) { is_paused_ = value; }; void setPaused(bool value) { is_paused_ = value; };

View File

@@ -1,72 +0,0 @@
#pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "s_moving_sprite.h" // Para SMovingSprite
class Surface; // lines 9-9
struct AnimationData
{
std::string name; // Nombre de la animacion
std::vector<SDL_Rect> frames; // Cada uno de los frames que componen la animación
int speed; // Velocidad de la animación
int loop; // Indica a que frame vuelve la animación al terminar. -1 para que no vuelva
bool completed; // Indica si ha finalizado la animación
int current_frame; // Frame actual
int counter; // Contador para las animaciones
AnimationData() : name(std::string()), speed(5), loop(0), completed(false), current_frame(0), counter(0) {}
};
using Animations = std::vector<std::string>;
// Carga las animaciones en un vector(Animations) desde un fichero
Animations loadAnimationsFromFile(const std::string &file_path);
class SAnimatedSprite : public SMovingSprite
{
protected:
// Variables
std::vector<AnimationData> animations_; // Vector con las diferentes animaciones
int current_animation_ = 0; // Animacion activa
// Calcula el frame correspondiente a la animación actual
void animate();
// Carga la animación desde un vector de cadenas
void setAnimations(const Animations &animations);
public:
// Constructor
SAnimatedSprite(std::shared_ptr<Surface> surface, const std::string &file_path);
SAnimatedSprite(std::shared_ptr<Surface> surface, const Animations &animations);
explicit SAnimatedSprite(std::shared_ptr<Surface> surface)
: SMovingSprite(surface) {}
// Destructor
virtual ~SAnimatedSprite() override = default;
// Actualiza las variables del objeto
void update() override;
// Comprueba si ha terminado la animación
bool animationIsCompleted();
// Obtiene el indice de la animación a partir del nombre
int getIndex(const std::string &name);
// Establece la animacion actual
void setCurrentAnimation(const std::string &name = "default");
void setCurrentAnimation(int index = 0);
// Reinicia la animación
void resetAnimation();
// Establece el frame actual de la animación
void setCurrentAnimationFrame(int num);
// Obtiene el numero de frames de la animación actual
int getCurrentAnimationSize() { return static_cast<int>(animations_[current_animation_].frames.size()); }
};

View File

@@ -1,82 +0,0 @@
#pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_render.h> // Para SDL_RendererFlip, SDL_FLIP_HORIZONTAL
#include <SDL2/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr
#include "s_sprite.h" // Para SSprite
class Surface; // lines 8-8
// Clase SMovingSprite. Añade movimiento y flip al sprite
class SMovingSprite : public SSprite
{
public:
protected:
float x_; // Posición en el eje X
float y_; // Posición en el eje Y
float vx_ = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
float vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles 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
SDL_RendererFlip flip_; // Indica como se voltea el sprite
// Mueve el sprite
void move();
public:
// Constructor
SMovingSprite(std::shared_ptr<Surface> surface, SDL_Rect pos, SDL_RendererFlip flip);
SMovingSprite(std::shared_ptr<Surface> surface, SDL_Rect pos);
explicit SMovingSprite(std::shared_ptr<Surface> surface);
// Destructor
virtual ~SMovingSprite() override = default;
// Actualiza las variables internas del objeto
virtual void update();
// Reinicia todas las variables a cero
void clear() override;
// Muestra el sprite por pantalla
void render() override;
void render(Uint8 source_color, Uint8 target_color) override;
// Obtiene la variable
float getPosX() const { return x_; }
float getPosY() const { return y_; }
float getVelX() const { return vx_; }
float getVelY() const { return vy_; }
float getAccelX() const { return ax_; }
float getAccelY() const { return ay_; }
// Establece la variable
void setVelX(float value) { vx_ = value; }
void setVelY(float value) { vy_ = value; }
void setAccelX(float value) { ax_ = value; }
void setAccelY(float value) { ay_ = value; }
// Establece el valor de la variable
void setFlip(SDL_RendererFlip flip) { flip_ = flip; }
// Gira el sprite horizontalmente
void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; }
// Obtiene el valor de la variable
SDL_RendererFlip getFlip() { return flip_; }
// Establece la posición y_ el tamaño del objeto
void setPos(SDL_Rect rect);
// Establece el valor de las variables
void setPos(float x, float y);
// Establece el valor de la variable
void setPosX(float value);
// Establece el valor de la variable
void setPosY(float value);
};

View File

@@ -1,70 +0,0 @@
#pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect, SDL_Point
#include <SDL2/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr
class Surface; // lines 5-5
// Clase SSprite
class SSprite
{
protected:
// Variables
std::shared_ptr<Surface> surface_; // Surface donde estan todos los dibujos del sprite
SDL_Rect pos_; // Posición y tamaño donde dibujar el sprite
SDL_Rect clip_; // Rectangulo de origen de la surface que se dibujará en pantalla
public:
// Constructor
SSprite(std::shared_ptr<Surface>, int x, int y, int w, int h);
SSprite(std::shared_ptr<Surface>, SDL_Rect rect);
explicit SSprite(std::shared_ptr<Surface>);
// Destructor
virtual ~SSprite() = default;
// Muestra el sprite por pantalla
virtual void render();
virtual void render(Uint8 source_color, Uint8 target_color);
// Reinicia las variables a cero
virtual void clear();
// Obtiene la posición y el tamaño
int getX() const { return pos_.x; }
int getY() const { return pos_.y; }
int getWidth() const { return pos_.w; }
int getHeight() const { return pos_.h; }
// Devuelve el rectangulo donde está el sprite
SDL_Rect getPosition() const { return pos_; }
SDL_Rect &getRect() { return pos_; }
// Establece la posición y el tamaño
void setX(int x) { pos_.x = x; }
void setY(int y) { pos_.y = y; }
void setWidth(int w) { pos_.w = w; }
void setHeight(int h) { pos_.h = h; }
// Establece la posición del objeto
void setPosition(int x, int y);
void setPosition(SDL_Point p);
void setPosition(SDL_Rect r) { pos_ = r; }
// Aumenta o disminuye la posición
void incX(int value) { pos_.x += value; }
void incY(int value) { pos_.y += value; }
// Obtiene el rectangulo que se dibuja de la surface
SDL_Rect getClip() const { return clip_; }
// Establece el rectangulo que se dibuja de la surface
void setClip(SDL_Rect rect) { clip_ = rect; }
void setClip(int x, int y, int w, int h) { clip_ = (SDL_Rect){x, y, w, h}; }
// Obtiene un puntero a la surface
std::shared_ptr<Surface> getSurface() const { return surface_; }
// Establece la surface a utilizar
void setSurface(std::shared_ptr<Surface> surface) { surface_ = surface; }
};

View File

@@ -1,6 +1,8 @@
#include "scoreboard.h" #include "scoreboard.h"
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include "defines.h" // Para BLOCK #include "defines.h" // Para BLOCK
#include "options.h" // Para Options, options, Cheat, OptionsGame #include "options.h" // Para Options, options, Cheat, OptionsGame
#include "resource.h" // Para Resource #include "resource.h" // Para Resource
@@ -14,8 +16,7 @@
Scoreboard::Scoreboard(std::shared_ptr<ScoreboardData> data) Scoreboard::Scoreboard(std::shared_ptr<ScoreboardData> data)
: item_surface_(Resource::get()->getSurface("items.gif")), : item_surface_(Resource::get()->getSurface("items.gif")),
data_(data), data_(data),
clock_(ClockData()) clock_(ClockData()) {
{
const int SURFACE_WIDTH_ = options.game.width; const int SURFACE_WIDTH_ = options.game.width;
constexpr int SURFACE_HEIGHT_ = 6 * BLOCK; constexpr int SURFACE_HEIGHT_ = 6 * BLOCK;
@@ -38,21 +39,18 @@ Scoreboard::Scoreboard(std::shared_ptr<ScoreboardData> data)
// Inicializa el vector de colores // Inicializa el vector de colores
const std::vector<std::string> COLORS = {"blue", "magenta", "green", "cyan", "yellow", "white", "bright_blue", "bright_magenta", "bright_green", "bright_cyan", "bright_yellow", "bright_white"}; const std::vector<std::string> COLORS = {"blue", "magenta", "green", "cyan", "yellow", "white", "bright_blue", "bright_magenta", "bright_green", "bright_cyan", "bright_yellow", "bright_white"};
for (const auto &color : COLORS) for (const auto& color : COLORS) {
{
color_.push_back(stringToColor(color)); color_.push_back(stringToColor(color));
} }
} }
// Pinta el objeto en pantalla // Pinta el objeto en pantalla
void Scoreboard::render() void Scoreboard::render() {
{
surface_->render(nullptr, &surface_dest_); surface_->render(nullptr, &surface_dest_);
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Scoreboard::update() void Scoreboard::update() {
{
counter_++; counter_++;
player_sprite_->update(); player_sprite_->update();
@@ -62,16 +60,14 @@ void Scoreboard::update()
// Dibuja la textura // Dibuja la textura
fillTexture(); fillTexture();
if (!is_paused_) if (!is_paused_) {
{
// Si está en pausa no se actualiza el reloj // Si está en pausa no se actualiza el reloj
clock_ = getTime(); clock_ = getTime();
} }
} }
// Obtiene el tiempo transcurrido de partida // Obtiene el tiempo transcurrido de partida
Scoreboard::ClockData Scoreboard::getTime() Scoreboard::ClockData Scoreboard::getTime() {
{
const Uint32 timeElapsed = SDL_GetTicks() - data_->ini_clock - paused_time_elapsed_; const Uint32 timeElapsed = SDL_GetTicks() - data_->ini_clock - paused_time_elapsed_;
ClockData time; ClockData time;
@@ -84,55 +80,43 @@ Scoreboard::ClockData Scoreboard::getTime()
} }
// Pone el marcador en modo pausa // Pone el marcador en modo pausa
void Scoreboard::setPaused(bool value) void Scoreboard::setPaused(bool value) {
{ if (is_paused_ == value) {
if (is_paused_ == value)
{
// Evita ejecutar lógica si el estado no cambia // Evita ejecutar lógica si el estado no cambia
return; return;
} }
is_paused_ = value; is_paused_ = value;
if (is_paused_) if (is_paused_) {
{
// Guarda el tiempo actual al pausar // Guarda el tiempo actual al pausar
paused_time_ = SDL_GetTicks(); paused_time_ = SDL_GetTicks();
} } else {
else
{
// Calcula el tiempo pausado acumulado al reanudar // Calcula el tiempo pausado acumulado al reanudar
paused_time_elapsed_ += SDL_GetTicks() - paused_time_; paused_time_elapsed_ += SDL_GetTicks() - paused_time_;
} }
} }
// Actualiza el color de la cantidad de items recogidos // Actualiza el color de la cantidad de items recogidos
void Scoreboard::updateItemsColor() void Scoreboard::updateItemsColor() {
{ if (!data_->jail_is_open) {
if (!data_->jail_is_open)
{
return; return;
} }
if (counter_ % 20 < 10) if (counter_ % 20 < 10) {
{
items_color_ = stringToColor("white"); items_color_ = stringToColor("white");
} } else {
else
{
items_color_ = stringToColor("magenta"); items_color_ = stringToColor("magenta");
} }
} }
// Devuelve la cantidad de minutos de juego transcurridos // Devuelve la cantidad de minutos de juego transcurridos
int Scoreboard::getMinutes() int Scoreboard::getMinutes() {
{
return getTime().minutes; return getTime().minutes;
} }
// Dibuja los elementos del marcador en la textura // Dibuja los elementos del marcador en la textura
void Scoreboard::fillTexture() void Scoreboard::fillTexture() {
{
// Empieza a dibujar en la textura // Empieza a dibujar en la textura
auto previuos_renderer = Screen::get()->getRendererSurface(); auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(surface_); Screen::get()->setRendererSurface(surface_);
@@ -149,16 +133,14 @@ void Scoreboard::fillTexture()
const int frame = desp % 4; const int frame = desp % 4;
player_sprite_->setCurrentAnimationFrame(frame); player_sprite_->setCurrentAnimationFrame(frame);
player_sprite_->setPosY(LINE2); player_sprite_->setPosY(LINE2);
for (int i = 0; i < data_->lives; ++i) for (int i = 0; i < data_->lives; ++i) {
{
player_sprite_->setPosX(8 + (16 * i) + desp); player_sprite_->setPosX(8 + (16 * i) + desp);
const int index = i % color_.size(); const int index = i % color_.size();
player_sprite_->render(1, color_.at(index)); player_sprite_->render(1, color_.at(index));
} }
// Muestra si suena la música // Muestra si suena la música
if (data_->music) if (data_->music) {
{
const Uint8 c = data_->color; const Uint8 c = data_->color;
SDL_Rect clip = {0, 8, 8, 8}; SDL_Rect clip = {0, 8, 8, 8};
item_surface_->renderWithColorReplace(20 * BLOCK, LINE2, 1, c, &clip); item_surface_->renderWithColorReplace(20 * BLOCK, LINE2, 1, c, &clip);

View File

@@ -1,15 +1,15 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_stdinc.h> // Para Uint32, Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint32, Uint8
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include <vector> // Para vector #include <vector> // Para vector
class SAnimatedSprite; // lines 10-10 class SAnimatedSprite; // lines 10-10
class Surface; // lines 11-11 class Surface; // lines 11-11
struct ScoreboardData struct ScoreboardData {
{
int items; // Lleva la cuenta de los objetos recogidos int items; // Lleva la cuenta de los objetos recogidos
int lives; // Lleva la cuenta de las vidas restantes del jugador int lives; // Lleva la cuenta de las vidas restantes del jugador
int rooms; // Lleva la cuenta de las habitaciones visitadas int rooms; // Lleva la cuenta de las habitaciones visitadas
@@ -20,18 +20,28 @@ struct ScoreboardData
// Constructor por defecto // Constructor por defecto
ScoreboardData() ScoreboardData()
: items(0), lives(0), rooms(0), music(true), color(0), ini_clock(0), jail_is_open(false) {} : items(0),
lives(0),
rooms(0),
music(true),
color(0),
ini_clock(0),
jail_is_open(false) {}
// Constructor parametrizado // Constructor parametrizado
ScoreboardData(int items, int lives, int rooms, bool music, Uint8 color, Uint32 ini_clock, bool jail_is_open) ScoreboardData(int items, int lives, int rooms, bool music, Uint8 color, Uint32 ini_clock, bool jail_is_open)
: items(items), lives(lives), rooms(rooms), music(music), color(color), ini_clock(ini_clock), jail_is_open(jail_is_open) {} : items(items),
lives(lives),
rooms(rooms),
music(music),
color(color),
ini_clock(ini_clock),
jail_is_open(jail_is_open) {}
}; };
class Scoreboard class Scoreboard {
{ private:
private: struct ClockData {
struct ClockData
{
int hours; int hours;
int minutes; int minutes;
int seconds; int seconds;
@@ -39,11 +49,17 @@ private:
// Constructor por defecto // Constructor por defecto
ClockData() ClockData()
: hours(0), minutes(0), seconds(0), separator(":") {} : hours(0),
minutes(0),
seconds(0),
separator(":") {}
// Constructor parametrizado // Constructor parametrizado
ClockData(int h, int m, int s, const std::string &sep) ClockData(int h, int m, int s, const std::string& sep)
: hours(h), minutes(m), seconds(s), separator(sep) {} : hours(h),
minutes(m),
seconds(s),
separator(sep) {}
}; };
// Objetos y punteros // Objetos y punteros
@@ -72,7 +88,7 @@ private:
// Dibuja los elementos del marcador en la surface // Dibuja los elementos del marcador en la surface
void fillTexture(); void fillTexture();
public: public:
// Constructor // Constructor
explicit Scoreboard(std::shared_ptr<ScoreboardData> data); explicit Scoreboard(std::shared_ptr<ScoreboardData> data);

View File

@@ -1,15 +1,18 @@
#include "screen.h" #include "screen.h"
#include <SDL2/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_events.h> // Para SDL_DISABLE, SDL_ENABLE #include <SDL3/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_mouse.h> // Para SDL_ShowCursor #include <SDL3/SDL_events.h> // Para SDL_DISABLE, SDL_ENABLE
#include <SDL2/SDL_pixels.h> // Para SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORM... #include <SDL3/SDL_mouse.h> // Para SDL_ShowCursor
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_pixels.h> // Para SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORM...
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <ctype.h> // Para toupper #include <ctype.h> // Para toupper
#include <algorithm> // Para max, min, transform #include <algorithm> // Para max, min, transform
#include <fstream> // Para basic_ostream, operator<<, endl, basic_... #include <fstream> // Para basic_ostream, operator<<, endl, basic_...
#include <iostream> // Para cerr #include <iostream> // Para cerr
#include <iterator> // Para istreambuf_iterator, operator== #include <iterator> // Para istreambuf_iterator, operator==
#include <string> // Para char_traits, string, operator+, operator== #include <string> // Para char_traits, string, operator+, operator==
#include "asset.h" // Para Asset, AssetType #include "asset.h" // Para Asset, AssetType
#include "jail_shader.h" // Para init, render #include "jail_shader.h" // Para init, render
#include "mouse.h" // Para updateCursorVisibility #include "mouse.h" // Para updateCursorVisibility
@@ -20,32 +23,28 @@
#include "text.h" // Para Text #include "text.h" // Para Text
// [SINGLETON] // [SINGLETON]
Screen *Screen::screen_ = nullptr; Screen* Screen::screen_ = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
void Screen::init(SDL_Window *window, SDL_Renderer *renderer) void Screen::init(SDL_Window* window, SDL_Renderer* renderer) {
{
Screen::screen_ = new Screen(window, renderer); Screen::screen_ = new Screen(window, renderer);
} }
// [SINGLETON] Destruiremos el objeto con esta función estática // [SINGLETON] Destruiremos el objeto con esta función estática
void Screen::destroy() void Screen::destroy() {
{
delete Screen::screen_; delete Screen::screen_;
} }
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
Screen *Screen::get() Screen* Screen::get() {
{
return Screen::screen_; return Screen::screen_;
} }
// Constructor // Constructor
Screen::Screen(SDL_Window *window, SDL_Renderer *renderer) Screen::Screen(SDL_Window* window, SDL_Renderer* renderer)
: window_(window), : window_(window),
renderer_(renderer), renderer_(renderer),
palettes_(Asset::get()->getListByType(AssetType::PALETTE)) palettes_(Asset::get()->getListByType(AssetType::PALETTE)) {
{
// Inicializa variables // Inicializa variables
SDL_DisplayMode DM; SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM); SDL_GetCurrentDisplayMode(0, &DM);
@@ -64,22 +63,18 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer)
// Crea la textura donde se dibujan los graficos del juego // Crea la textura donde se dibujan los graficos del juego
game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, options.game.width, options.game.height); game_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, options.game.width, options.game.height);
if (!game_texture_) if (!game_texture_) {
{
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (options.console) if (options.console) {
{
std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl; std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
} }
} }
// Crea la textura donde se dibuja el borde que rodea el area de juego // Crea la textura donde se dibuja el borde que rodea el area de juego
border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, options.game.width + options.video.border.width * 2, options.game.height + options.video.border.height * 2); border_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, options.game.width + options.video.border.width * 2, options.game.height + options.video.border.height * 2);
if (!border_texture_) if (!border_texture_) {
{
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (options.console) if (options.console) {
{
std::cerr << "Error: border_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl; std::cerr << "Error: border_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
} }
} }
@@ -88,11 +83,9 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer)
const int EXTRA_WIDTH = options.video.border.enabled ? options.video.border.width * 2 : 0; const int EXTRA_WIDTH = options.video.border.enabled ? options.video.border.width * 2 : 0;
const int EXTRA_HEIGHT = options.video.border.enabled ? options.video.border.height * 2 : 0; const int EXTRA_HEIGHT = options.video.border.enabled ? options.video.border.height * 2 : 0;
shaders_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, options.game.width + EXTRA_WIDTH, options.game.height + EXTRA_HEIGHT); shaders_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, options.game.width + EXTRA_WIDTH, options.game.height + EXTRA_HEIGHT);
if (!shaders_texture_) if (!shaders_texture_) {
{
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (options.console) if (options.console) {
{
std::cerr << "Error: shaders_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl; std::cerr << "Error: shaders_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
} }
} }
@@ -122,16 +115,14 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer)
} }
// Destructor // Destructor
Screen::~Screen() Screen::~Screen() {
{
SDL_DestroyTexture(game_texture_); SDL_DestroyTexture(game_texture_);
SDL_DestroyTexture(border_texture_); SDL_DestroyTexture(border_texture_);
SDL_DestroyTexture(shaders_texture_); SDL_DestroyTexture(shaders_texture_);
} }
// Limpia el renderer // Limpia el renderer
void Screen::clearRenderer(Color color) void Screen::clearRenderer(Color color) {
{
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF); SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF);
SDL_RenderClear(renderer_); SDL_RenderClear(renderer_);
} }
@@ -140,8 +131,7 @@ void Screen::clearRenderer(Color color)
void Screen::start() { setRendererSurface(nullptr); } void Screen::start() { setRendererSurface(nullptr); }
// Vuelca el contenido del renderizador en pantalla // Vuelca el contenido del renderizador en pantalla
void Screen::render() void Screen::render() {
{
fps_.increment(); fps_.increment();
// Renderiza todos los overlays // Renderiza todos los overlays
@@ -155,8 +145,7 @@ void Screen::render()
} }
// Establece el modo de video // Establece el modo de video
void Screen::setVideoMode(int mode) void Screen::setVideoMode(int mode) {
{
// Actualiza las opciones // Actualiza las opciones
options.video.mode = mode; options.video.mode = mode;
@@ -170,23 +159,19 @@ void Screen::setVideoMode(int mode)
} }
// Camibia entre pantalla completa y ventana // Camibia entre pantalla completa y ventana
void Screen::toggleVideoMode() void Screen::toggleVideoMode() {
{
options.video.mode = (options.video.mode == 0) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0; options.video.mode = (options.video.mode == 0) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
setVideoMode(options.video.mode); setVideoMode(options.video.mode);
} }
// Reduce el tamaño de la ventana // Reduce el tamaño de la ventana
bool Screen::decWindowZoom() bool Screen::decWindowZoom() {
{ if (options.video.mode == 0) {
if (options.video.mode == 0)
{
const int PREVIOUS_ZOOM = options.window.zoom; const int PREVIOUS_ZOOM = options.window.zoom;
--options.window.zoom; --options.window.zoom;
options.window.zoom = std::max(options.window.zoom, 1); options.window.zoom = std::max(options.window.zoom, 1);
if (options.window.zoom != PREVIOUS_ZOOM) if (options.window.zoom != PREVIOUS_ZOOM) {
{
setVideoMode(options.video.mode); setVideoMode(options.video.mode);
return true; return true;
} }
@@ -196,16 +181,13 @@ bool Screen::decWindowZoom()
} }
// Aumenta el tamaño de la ventana // Aumenta el tamaño de la ventana
bool Screen::incWindowZoom() bool Screen::incWindowZoom() {
{ if (options.video.mode == 0) {
if (options.video.mode == 0)
{
const int PREVIOUS_ZOOM = options.window.zoom; const int PREVIOUS_ZOOM = options.window.zoom;
++options.window.zoom; ++options.window.zoom;
options.window.zoom = std::min(options.window.zoom, options.window.max_zoom); options.window.zoom = std::min(options.window.zoom, options.window.max_zoom);
if (options.window.zoom != PREVIOUS_ZOOM) if (options.window.zoom != PREVIOUS_ZOOM) {
{
setVideoMode(options.video.mode); setVideoMode(options.video.mode);
return true; return true;
} }
@@ -215,55 +197,47 @@ bool Screen::incWindowZoom()
} }
// Cambia el color del borde // Cambia el color del borde
void Screen::setBorderColor(Uint8 color) void Screen::setBorderColor(Uint8 color) {
{
border_color_ = color; border_color_ = color;
border_surface_->clear(border_color_); border_surface_->clear(border_color_);
} }
// Cambia entre borde visible y no visible // Cambia entre borde visible y no visible
void Screen::toggleBorder() void Screen::toggleBorder() {
{
options.video.border.enabled = !options.video.border.enabled; options.video.border.enabled = !options.video.border.enabled;
createShadersTexture(); createShadersTexture();
setVideoMode(options.video.mode); setVideoMode(options.video.mode);
} }
// Dibuja las notificaciones // Dibuja las notificaciones
void Screen::renderNotifications() void Screen::renderNotifications() {
{ if (notifications_enabled_) {
if (notifications_enabled_)
{
Notifier::get()->render(); Notifier::get()->render();
} }
} }
// Cambia el estado de los shaders // Cambia el estado de los shaders
void Screen::toggleShaders() void Screen::toggleShaders() {
{
options.video.shaders = !options.video.shaders; options.video.shaders = !options.video.shaders;
setVideoMode(options.video.mode); setVideoMode(options.video.mode);
} }
// Actualiza la lógica de la clase // Actualiza la lógica de la clase
void Screen::update() void Screen::update() {
{
fps_.calculate(SDL_GetTicks()); fps_.calculate(SDL_GetTicks());
Notifier::get()->update(); Notifier::get()->update();
Mouse::updateCursorVisibility(); Mouse::updateCursorVisibility();
} }
// Calcula el tamaño de la ventana // Calcula el tamaño de la ventana
void Screen::adjustWindowSize() void Screen::adjustWindowSize() {
{
window_width_ = options.game.width + (options.video.border.enabled ? options.video.border.width * 2 : 0); window_width_ = options.game.width + (options.video.border.enabled ? options.video.border.width * 2 : 0);
window_height_ = options.game.height + (options.video.border.enabled ? options.video.border.height * 2 : 0); window_height_ = options.game.height + (options.video.border.enabled ? options.video.border.height * 2 : 0);
options.window.max_zoom = getMaxZoom(); options.window.max_zoom = getMaxZoom();
// Establece el nuevo tamaño // Establece el nuevo tamaño
if (options.video.mode == 0) if (options.video.mode == 0) {
{
int old_width, old_height; int old_width, old_height;
SDL_GetWindowSize(window_, &old_width, &old_height); SDL_GetWindowSize(window_, &old_width, &old_height);
@@ -282,8 +256,7 @@ void Screen::adjustWindowSize()
void Screen::adjustRenderLogicalSize() { SDL_RenderSetLogicalSize(renderer_, window_width_, window_height_); } void Screen::adjustRenderLogicalSize() { SDL_RenderSetLogicalSize(renderer_, window_width_, window_height_); }
// Obtiene el tamaño máximo de zoom posible para la ventana // Obtiene el tamaño máximo de zoom posible para la ventana
int Screen::getMaxZoom() int Screen::getMaxZoom() {
{
// Obtiene información sobre la pantalla // Obtiene información sobre la pantalla
SDL_DisplayMode DM; SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM); SDL_GetCurrentDisplayMode(0, &DM);
@@ -298,10 +271,8 @@ int Screen::getMaxZoom()
} }
// Reinicia los shaders // Reinicia los shaders
void Screen::resetShaders() void Screen::resetShaders() {
{ if (options.video.shaders) {
if (options.video.shaders)
{
const std::string GLSL_FILE = options.video.border.enabled ? "crtpi_240.glsl" : "crtpi_192.glsl"; const std::string GLSL_FILE = options.video.border.enabled ? "crtpi_240.glsl" : "crtpi_192.glsl";
std::ifstream f(Asset::get()->get(GLSL_FILE).c_str()); std::ifstream f(Asset::get()->get(GLSL_FILE).c_str());
std::string source((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>()); std::string source((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
@@ -311,17 +282,14 @@ void Screen::resetShaders()
} }
// Establece el renderizador para las surfaces // Establece el renderizador para las surfaces
void Screen::setRendererSurface(std::shared_ptr<Surface> surface) void Screen::setRendererSurface(std::shared_ptr<Surface> surface) {
{
(surface) ? renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(surface) : renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_); (surface) ? renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(surface) : renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
} }
// Cambia la paleta // Cambia la paleta
void Screen::nextPalette() void Screen::nextPalette() {
{
++current_palette_; ++current_palette_;
if (current_palette_ == static_cast<int>(palettes_.size())) if (current_palette_ == static_cast<int>(palettes_.size())) {
{
current_palette_ = 0; current_palette_ = 0;
} }
@@ -329,14 +297,10 @@ void Screen::nextPalette()
} }
// Cambia la paleta // Cambia la paleta
void Screen::previousPalette() void Screen::previousPalette() {
{ if (current_palette_ > 0) {
if (current_palette_ > 0)
{
--current_palette_; --current_palette_;
} } else {
else
{
current_palette_ = static_cast<Uint8>(palettes_.size() - 1); current_palette_ = static_cast<Uint8>(palettes_.size() - 1);
} }
@@ -344,8 +308,7 @@ void Screen::previousPalette()
} }
// Establece la paleta // Establece la paleta
void Screen::setPalete() void Screen::setPalete() {
{
game_surface_->loadPalette(Resource::get()->getPalette(palettes_.at(current_palette_))); game_surface_->loadPalette(Resource::get()->getPalette(palettes_.at(current_palette_)));
border_surface_->loadPalette(Resource::get()->getPalette(palettes_.at(current_palette_))); border_surface_->loadPalette(Resource::get()->getPalette(palettes_.at(current_palette_)));
@@ -353,8 +316,7 @@ void Screen::setPalete()
// Eliminar ".gif" // Eliminar ".gif"
size_t pos = options.video.palette.find(".pal"); size_t pos = options.video.palette.find(".pal");
if (pos != std::string::npos) if (pos != std::string::npos) {
{
options.video.palette.erase(pos, 4); options.video.palette.erase(pos, 4);
} }
@@ -363,42 +325,32 @@ void Screen::setPalete()
} }
// Extrae los nombres de las paletas // Extrae los nombres de las paletas
void Screen::processPaletteList() void Screen::processPaletteList() {
{ for (auto& palette : palettes_) {
for (auto &palette : palettes_)
{
palette = getFileName(palette); palette = getFileName(palette);
} }
} }
// Copia la surface a la textura // Copia la surface a la textura
void Screen::surfaceToTexture() void Screen::surfaceToTexture() {
{ if (options.video.border.enabled) {
if (options.video.border.enabled)
{
border_surface_->copyToTexture(renderer_, border_texture_); border_surface_->copyToTexture(renderer_, border_texture_);
game_surface_->copyToTexture(renderer_, border_texture_, nullptr, &game_surface_dstrect_); game_surface_->copyToTexture(renderer_, border_texture_, nullptr, &game_surface_dstrect_);
} } else {
else
{
game_surface_->copyToTexture(renderer_, game_texture_); game_surface_->copyToTexture(renderer_, game_texture_);
} }
} }
// Copia la textura al renderizador // Copia la textura al renderizador
void Screen::textureToRenderer() void Screen::textureToRenderer() {
{ SDL_Texture* texture_to_render = options.video.border.enabled ? border_texture_ : game_texture_;
SDL_Texture *texture_to_render = options.video.border.enabled ? border_texture_ : game_texture_;
if (options.video.shaders) if (options.video.shaders) {
{
SDL_SetRenderTarget(renderer_, shaders_texture_); SDL_SetRenderTarget(renderer_, shaders_texture_);
SDL_RenderCopy(renderer_, texture_to_render, nullptr, nullptr); SDL_RenderCopy(renderer_, texture_to_render, nullptr, nullptr);
SDL_SetRenderTarget(renderer_, nullptr); SDL_SetRenderTarget(renderer_, nullptr);
shader::render(); shader::render();
} } else {
else
{
SDL_SetRenderTarget(renderer_, nullptr); SDL_SetRenderTarget(renderer_, nullptr);
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF); SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer_); SDL_RenderClear(renderer_);
@@ -408,21 +360,17 @@ void Screen::textureToRenderer()
} }
// Renderiza todos los overlays // Renderiza todos los overlays
void Screen::renderOverlays() void Screen::renderOverlays() {
{
renderNotifications(); renderNotifications();
renderInfo(); renderInfo();
} }
// Localiza la paleta dentro del vector de paletas // Localiza la paleta dentro del vector de paletas
size_t Screen::findPalette(const std::string &name) size_t Screen::findPalette(const std::string& name) {
{
std::string upper_name = toUpper(name + ".pal"); std::string upper_name = toUpper(name + ".pal");
for (size_t i = 0; i < palettes_.size(); ++i) for (size_t i = 0; i < palettes_.size(); ++i) {
{ if (toUpper(getFileName(palettes_[i])) == upper_name) {
if (toUpper(getFileName(palettes_[i])) == upper_name)
{
return i; return i;
} }
} }
@@ -430,10 +378,8 @@ size_t Screen::findPalette(const std::string &name)
} }
// Recrea la textura para los shaders // Recrea la textura para los shaders
void Screen::createShadersTexture() void Screen::createShadersTexture() {
{ if (shaders_texture_) {
if (shaders_texture_)
{
SDL_DestroyTexture(shaders_texture_); SDL_DestroyTexture(shaders_texture_);
} }
@@ -441,21 +387,17 @@ void Screen::createShadersTexture()
const int EXTRA_WIDTH = options.video.border.enabled ? options.video.border.width * 2 : 0; const int EXTRA_WIDTH = options.video.border.enabled ? options.video.border.width * 2 : 0;
const int EXTRA_HEIGHT = options.video.border.enabled ? options.video.border.height * 2 : 0; const int EXTRA_HEIGHT = options.video.border.enabled ? options.video.border.height * 2 : 0;
shaders_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, options.game.width + EXTRA_WIDTH, options.game.height + EXTRA_HEIGHT); shaders_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, options.game.width + EXTRA_WIDTH, options.game.height + EXTRA_HEIGHT);
if (!shaders_texture_) if (!shaders_texture_) {
{
// Registrar el error si está habilitado // Registrar el error si está habilitado
if (options.console) if (options.console) {
{
std::cerr << "Error: shaders_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl; std::cerr << "Error: shaders_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
} }
} }
} }
// Muestra información por pantalla // Muestra información por pantalla
void Screen::renderInfo() void Screen::renderInfo() {
{ if (show_debug_info_ && Resource::get()) {
if (show_debug_info_ && Resource::get())
{
auto text = Resource::get()->getText("smb2"); auto text = Resource::get()->getText("smb2");
auto color = static_cast<Uint8>(PaletteColor::YELLOW); auto color = static_cast<Uint8>(PaletteColor::YELLOW);
@@ -493,6 +435,6 @@ void Screen::setNotificationsEnabled(bool value) { notifications_enabled_ = valu
void Screen::toggleDebugInfo() { show_debug_info_ = !show_debug_info_; } void Screen::toggleDebugInfo() { show_debug_info_ = !show_debug_info_; }
// Getters // Getters
SDL_Renderer *Screen::getRenderer() { return renderer_; } SDL_Renderer* Screen::getRenderer() { return renderer_; }
std::shared_ptr<Surface> Screen::getRendererSurface() { return (*renderer_surface_); } std::shared_ptr<Surface> Screen::getRendererSurface() { return (*renderer_surface_); }
std::shared_ptr<Surface> Screen::getBorderSurface() { return border_surface_; } std::shared_ptr<Surface> Screen::getBorderSurface() { return border_surface_; }

View File

@@ -1,49 +1,49 @@
#pragma once #pragma once
#include <SDL2/SDL_blendmode.h> // Para SDL_BlendMode #include <SDL3/SDL_blendmode.h> // Para SDL_BlendMode
#include <SDL2/SDL_rect.h> // Para SDL_Rect #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_render.h> // Para SDL_Renderer, SDL_Texture #include <SDL3/SDL_render.h> // Para SDL_Renderer, SDL_Texture
#include <SDL2/SDL_stdinc.h> // Para Uint8, Uint32 #include <SDL3/SDL_stdinc.h> // Para Uint8, Uint32
#include <SDL2/SDL_video.h> // Para SDL_Window #include <SDL3/SDL_video.h> // Para SDL_Window
#include <stddef.h> // Para size_t #include <stddef.h> // Para size_t
#include <memory> // Para shared_ptr, __shared_ptr_access #include <memory> // Para shared_ptr, __shared_ptr_access
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
#include "utils.h" // Para Color #include "utils.h" // Para Color
struct Surface; struct Surface;
// Tipos de filtro // Tipos de filtro
enum class ScreenFilter : Uint32 enum class ScreenFilter : Uint32 {
{
NEAREST = 0, NEAREST = 0,
LINEAR = 1, LINEAR = 1,
}; };
class Screen class Screen {
{ private:
private:
// Constantes // Constantes
static constexpr int WINDOWS_DECORATIONS_ = 35; static constexpr int WINDOWS_DECORATIONS_ = 35;
// Estructuras // Estructuras
struct FPS struct FPS {
{
Uint32 ticks; // Tiempo en milisegundos desde que se comenzó a contar. Uint32 ticks; // Tiempo en milisegundos desde que se comenzó a contar.
int frameCount; // Número acumulado de frames en el intervalo. int frameCount; // Número acumulado de frames en el intervalo.
int lastValue; // Número de frames calculado en el último segundo. int lastValue; // Número de frames calculado en el último segundo.
// Constructor para inicializar la estructura. // Constructor para inicializar la estructura.
FPS() : ticks(0), frameCount(0), lastValue(0) {} FPS()
: ticks(0),
frameCount(0),
lastValue(0) {}
// Incrementador que se llama en cada frame. // Incrementador que se llama en cada frame.
void increment() void increment() {
{
frameCount++; frameCount++;
} }
// Método para calcular y devolver el valor de FPS. // Método para calcular y devolver el valor de FPS.
int calculate(Uint32 currentTicks) int calculate(Uint32 currentTicks) {
{
if (currentTicks - ticks >= 1000) // Si ha pasado un segundo o más. if (currentTicks - ticks >= 1000) // Si ha pasado un segundo o más.
{ {
lastValue = frameCount; // Actualizamos el valor del último FPS. lastValue = frameCount; // Actualizamos el valor del último FPS.
@@ -55,14 +55,14 @@ private:
}; };
// [SINGLETON] Objeto privado // [SINGLETON] Objeto privado
static Screen *screen_; static Screen* screen_;
// Objetos y punteros // Objetos y punteros
SDL_Window *window_; // Ventana de la aplicación SDL_Window* window_; // Ventana de la aplicación
SDL_Renderer *renderer_; // El renderizador de la ventana SDL_Renderer* renderer_; // El renderizador de la ventana
SDL_Texture *game_texture_; // Textura donde se dibuja el juego SDL_Texture* game_texture_; // Textura donde se dibuja el juego
SDL_Texture *border_texture_; // Textura donde se dibuja el borde del juego SDL_Texture* border_texture_; // Textura donde se dibuja el borde del juego
SDL_Texture *shaders_texture_; // Textura para aplicar los shaders SDL_Texture* shaders_texture_; // Textura para aplicar los shaders
std::shared_ptr<Surface> game_surface_; // Surface principal para manejar game_surface_data_ std::shared_ptr<Surface> game_surface_; // Surface principal para manejar game_surface_data_
std::shared_ptr<Surface> border_surface_; // Surface para pintar el el borde de la pantalla std::shared_ptr<Surface> border_surface_; // Surface para pintar el el borde de la pantalla
std::shared_ptr<std::shared_ptr<Surface>> renderer_surface_; // Puntero a la Surface que actua std::shared_ptr<std::shared_ptr<Surface>> renderer_surface_; // Puntero a la Surface que actua
@@ -109,7 +109,7 @@ private:
void renderOverlays(); void renderOverlays();
// Localiza la paleta dentro del vector de paletas // Localiza la paleta dentro del vector de paletas
size_t findPalette(const std::string &name); size_t findPalette(const std::string& name);
// Recrea la textura para los shaders // Recrea la textura para los shaders
void createShadersTexture(); void createShadersTexture();
@@ -118,20 +118,20 @@ private:
void renderInfo(); void renderInfo();
// Constructor // Constructor
Screen(SDL_Window *window, SDL_Renderer *renderer); Screen(SDL_Window* window, SDL_Renderer* renderer);
// Destructor // Destructor
~Screen(); ~Screen();
public: public:
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
static void init(SDL_Window *window, SDL_Renderer *renderer); static void init(SDL_Window* window, SDL_Renderer* renderer);
// [SINGLETON] Destruiremos el objeto con esta función estática // [SINGLETON] Destruiremos el objeto con esta función estática
static void destroy(); static void destroy();
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
static Screen *get(); static Screen* get();
// Limpia el renderer // Limpia el renderer
void clearRenderer(Color color = {0x00, 0x00, 0x00}); void clearRenderer(Color color = {0x00, 0x00, 0x00});
@@ -204,7 +204,7 @@ public:
void toggleDebugInfo(); void toggleDebugInfo();
// Getters // Getters
SDL_Renderer *getRenderer(); SDL_Renderer* getRenderer();
std::shared_ptr<Surface> getRendererSurface(); std::shared_ptr<Surface> getRendererSurface();
std::shared_ptr<Surface> getBorderSurface(); std::shared_ptr<Surface> getBorderSurface();
}; };

259
source/sections/credits.cpp Normal file
View File

@@ -0,0 +1,259 @@
#include "credits.h"
#include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para min
#include "defines.h" // Para GAME_SPEED, PLAY_AREA_CENTER_X, PLAY_...
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "options.h" // Para Options, options, OptionsGame, Sectio...
#include "resource.h" // Para Resource
#include "s_animated_sprite.h" // Para SAnimatedSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "utils.h" // Para PaletteColor
// Constructor
Credits::Credits()
: shining_sprite_(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface("shine.gif"), Resource::get()->getAnimations("shine.ani"))) {
// Inicializa variables
options.section.section = Section::CREDITS;
options.section.subsection = Subsection::NONE;
shining_sprite_->setPos({194, 174, 8, 8});
// Cambia el color del borde
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
// Crea la textura para el texto que se escribe en pantalla
text_surface_ = std::make_shared<Surface>(options.game.width, options.game.height);
// Crea la textura para cubrir el rexto
cover_surface_ = std::make_shared<Surface>(options.game.width, options.game.height);
// Escribe el texto en la textura
fillTexture();
}
// Comprueba el manejador de eventos
void Credits::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
globalEvents::check(event);
}
}
// Comprueba las entradas
void Credits::checkInput() {
globalInputs::check();
}
// Inicializa los textos
void Credits::iniTexts() {
#ifndef GAME_CONSOLE
std::string keys = "";
switch (options.keys) {
case ControlScheme::CURSOR:
keys = "CURSORS";
break;
case ControlScheme::OPQA:
keys = "O,P AND Q";
break;
case ControlScheme::WASD:
keys = "A,D AND W";
break;
default:
break;
}
texts_.clear();
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"INSTRUCTIONS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"HELP JAILDOC TO GET BACK ALL", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"HIS PROJECTS AND GO TO THE", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"JAIL TO FINISH THEM", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"KEYS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({keys + " TO MOVE AND JUMP", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"M TO SWITCH THE MUSIC", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"H TO PAUSE THE GAME", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"F1-F2 TO CHANGE WINDOWS SIZE", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"F3 TO SWITCH TO FULLSCREEN", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"B TO TOOGLE THE BORDER SCREEN", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"A GAME BY JAILDESIGNER", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"MADE ON SUMMER/FALL 2022", static_cast<Uint8>(PaletteColor::YELLOW)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"I LOVE JAILGAMES! ", static_cast<Uint8>(PaletteColor::WHITE)});
texts_.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
#else
texts.clear();
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"INSTRUCTIONS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"HELP JAILDOC TO GET BACK ALL", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"HIS PROJECTS AND GO TO THE", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"JAIL TO FINISH THEM", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"KEYS:", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"B TO JUMP", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"R TO SWITCH THE MUSIC", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"L TO SWAP THE COLOR PALETTE", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"START TO PAUSE", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"SELECT TO EXIT", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"A GAME BY JAILDESIGNER", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"MADE ON SUMMER/FALL 2022", static_cast<Uint8>(PaletteColor::YELLOW)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"I LOVE JAILGAMES! ", static_cast<Uint8>(PaletteColor::WHITE)});
texts.push_back({"", static_cast<Uint8>(PaletteColor::WHITE)});
#endif
}
// Escribe el texto en la textura
void Credits::fillTexture() {
// Inicializa los textos
iniTexts();
// Rellena la textura de texto
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(text_surface_);
text_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
auto text = Resource::get()->getText("smb2");
// Escribe el texto en la textura
const int SIZE = text->getCharacterSize();
int pos_y = 0;
for (const auto& t : texts_) {
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, pos_y * SIZE, t.label, 1, t.color);
pos_y++;
}
// Escribe el corazón
const int TEXT_LENGHT = text->lenght(texts_[22].label, 1) - text->lenght(" ", 1); // Se resta el ultimo caracter que es un espacio
const int POS_X = ((PLAY_AREA_WIDTH - TEXT_LENGHT) / 2) + TEXT_LENGHT;
text->writeColored(POS_X, 176, "}", static_cast<Uint8>(PaletteColor::BRIGHT_RED));
Screen::get()->setRendererSurface(previuos_renderer);
// Recoloca el sprite del brillo
shining_sprite_->setPosX(POS_X + 2);
// Rellena la textura que cubre el texto con color transparente
cover_surface_->clear(static_cast<Uint8>(PaletteColor::TRANSPARENT));
// Los primeros 8 pixels crea una malla
auto color = static_cast<Uint8>(PaletteColor::BLACK);
for (int i = 0; i < 256; i += 2) {
cover_surface_->putPixel(i, 0, color);
cover_surface_->putPixel(i, 2, color);
cover_surface_->putPixel(i, 4, color);
cover_surface_->putPixel(i, 6, color);
cover_surface_->putPixel(i + 1, 5, color);
cover_surface_->putPixel(i + 1, 7, color);
}
// El resto se rellena de color sólido
SDL_Rect rect = {0, 8, 256, 192};
cover_surface_->fillRect(&rect, color);
}
// Actualiza el contador
void Credits::updateCounter() {
// Incrementa el contador
if (counter_enabled_) {
counter_++;
if (counter_ == 224 || counter_ == 544 || counter_ == 672) {
counter_enabled_ = false;
}
} else {
sub_counter_++;
if (sub_counter_ == 100) {
counter_enabled_ = true;
sub_counter_ = 0;
}
}
// Comprueba si ha terminado la sección
if (counter_ > 1200) {
options.section.section = Section::DEMO;
}
}
// Actualiza las variables
void Credits::update() {
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba las entradas
checkInput();
// Actualiza el contador
updateCounter();
Screen::get()->update();
// Actualiza el sprite con el brillo
if (counter_ > 770) {
shining_sprite_->update();
}
}
}
// Dibuja en pantalla
void Credits::render() {
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
// Limpia la pantalla
Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));
if (counter_ < 1150) {
// Dibuja la textura con el texto en pantalla
text_surface_->render(0, 0);
// Dibuja la textura que cubre el texto
const int offset = std::min(counter_ / 8, 192 / 2);
SDL_Rect srcRect = {0, 0, 256, 192 - (offset * 2)};
cover_surface_->render(0, offset * 2, &srcRect);
// Dibuja el sprite con el brillo
shining_sprite_->render(1, static_cast<Uint8>(PaletteColor::BRIGHT_WHITE));
}
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void Credits::run() {
while (options.section.section == Section::CREDITS) {
update();
checkEvents();
render();
}
}

60
source/sections/credits.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#include <SDL3/SDL_stdinc.h> // Para Uint32, Uint8
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
class SAnimatedSprite; // lines 11-11
class Surface;
class Credits {
private:
struct Captions {
std::string label; // Texto a escribir
Uint8 color; // Color del texto
};
// Objetos y punteros
std::shared_ptr<Surface> text_surface_; // Textura para dibujar el texto
std::shared_ptr<Surface> cover_surface_; // Textura para cubrir el texto
std::shared_ptr<SAnimatedSprite> shining_sprite_; // Sprite para el brillo del corazón
// Variables
int counter_ = 0; // Contador
bool counter_enabled_ = true; // Indica si esta activo el contador
int sub_counter_ = 0; // Contador secundario
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<Captions> texts_; // Vector con los textos
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Actualiza el contador
void updateCounter();
// Inicializa los textos
void iniTexts();
// Escribe el texto en la textura
void fillTexture();
public:
// Constructor
Credits();
// Destructor
~Credits() = default;
// Bucle principal
void run();
};

View File

@@ -1,8 +1,11 @@
#include "ending.h" #include "ending.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_rect.h> // Para SDL_Rect #include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para min #include <algorithm> // Para min
#include "defines.h" // Para GAME_SPEED #include "defines.h" // Para GAME_SPEED
#include "global_events.h" // Para check #include "global_events.h" // Para check
#include "global_inputs.h" // Para check #include "global_inputs.h" // Para check
@@ -21,8 +24,7 @@ Ending::Ending()
pre_counter_(0), pre_counter_(0),
cover_counter_(0), cover_counter_(0),
ticks_(0), ticks_(0),
current_scene_(0) current_scene_(0) {
{
options.section.section = Section::ENDING; options.section.section = Section::ENDING;
options.section.subsection = Subsection::NONE; options.section.subsection = Subsection::NONE;
@@ -46,11 +48,9 @@ Ending::Ending()
} }
// Actualiza el objeto // Actualiza el objeto
void Ending::update() void Ending::update() {
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
{
// Actualiza el contador de ticks // Actualiza el contador de ticks
ticks_ = SDL_GetTicks(); ticks_ = SDL_GetTicks();
@@ -75,8 +75,7 @@ void Ending::update()
} }
// Dibuja el final en pantalla // Dibuja el final en pantalla
void Ending::render() void Ending::render() {
{
// Prepara para empezar a dibujar en la textura de juego // Prepara para empezar a dibujar en la textura de juego
Screen::get()->start(); Screen::get()->start();
@@ -88,10 +87,8 @@ void Ending::render()
sprite_pics_.at(current_scene_).cover_sprite->render(); sprite_pics_.at(current_scene_).cover_sprite->render();
// Dibuja los textos de la escena // Dibuja los textos de la escena
for (const auto &ti : scenes_.at(current_scene_).text_index) for (const auto& ti : scenes_.at(current_scene_).text_index) {
{ if (counter_ > ti.trigger) {
if (counter_ > ti.trigger)
{
sprite_texts_.at(ti.index).image_sprite->render(); sprite_texts_.at(ti.index).image_sprite->render();
sprite_texts_.at(ti.index).cover_sprite->render(); sprite_texts_.at(ti.index).cover_sprite->render();
} }
@@ -105,24 +102,20 @@ void Ending::render()
} }
// Comprueba el manejador de eventos // Comprueba el manejador de eventos
void Ending::checkEvents() void Ending::checkEvents() {
{
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event)) {
{
globalEvents::check(event); globalEvents::check(event);
} }
} }
// Comprueba las entradas // Comprueba las entradas
void Ending::checkInput() void Ending::checkInput() {
{
globalInputs::check(); globalInputs::check();
} }
// Inicializa los textos // Inicializa los textos
void Ending::iniTexts() void Ending::iniTexts() {
{
// Vector con los textos // Vector con los textos
std::vector<TextAndPosition> texts; std::vector<TextAndPosition> texts;
@@ -158,8 +151,7 @@ void Ending::iniTexts()
// Crea los sprites // Crea los sprites
sprite_texts_.clear(); sprite_texts_.clear();
for (const auto &txt : texts) for (const auto& txt : texts) {
{
auto text = Resource::get()->getText("smb2"); auto text = Resource::get()->getText("smb2");
const int WIDTH = text->lenght(txt.caption, 1) + 2 + 2; const int WIDTH = text->lenght(txt.caption, 1) + 2 + 2;
@@ -189,8 +181,7 @@ void Ending::iniTexts()
// Crea una malla de 8 pixels de alto // Crea una malla de 8 pixels de alto
auto surface = Screen::get()->getRendererSurface(); auto surface = Screen::get()->getRendererSurface();
auto color = static_cast<Uint8>(PaletteColor::BLACK); auto color = static_cast<Uint8>(PaletteColor::BLACK);
for (int i = 0; i < WIDTH; i += 2) for (int i = 0; i < WIDTH; i += 2) {
{
surface->putPixel(i, 0, color); surface->putPixel(i, 0, color);
surface->putPixel(i, 2, color); surface->putPixel(i, 2, color);
surface->putPixel(i, 4, color); surface->putPixel(i, 4, color);
@@ -219,8 +210,7 @@ void Ending::iniTexts()
} }
// Inicializa las imagenes // Inicializa las imagenes
void Ending::iniPics() void Ending::iniPics() {
{
// Vector con las rutas y la posición // Vector con las rutas y la posición
std::vector<TextAndPosition> pics; std::vector<TextAndPosition> pics;
@@ -233,8 +223,7 @@ void Ending::iniPics()
// Crea los sprites // Crea los sprites
sprite_pics_.clear(); sprite_pics_.clear();
for (const auto &pic : pics) for (const auto& pic : pics) {
{
EndingSurface sp; EndingSurface sp;
// Crea la texture // Crea la texture
@@ -258,8 +247,7 @@ void Ending::iniPics()
// Crea una malla en los primeros 8 pixels // Crea una malla en los primeros 8 pixels
auto surface = Screen::get()->getRendererSurface(); auto surface = Screen::get()->getRendererSurface();
auto color = static_cast<Uint8>(PaletteColor::BLACK); auto color = static_cast<Uint8>(PaletteColor::BLACK);
for (int i = 0; i < WIDTH; i += 2) for (int i = 0; i < WIDTH; i += 2) {
{
surface->putPixel(i, 0, color); surface->putPixel(i, 0, color);
surface->putPixel(i, 2, color); surface->putPixel(i, 2, color);
surface->putPixel(i, 4, color); surface->putPixel(i, 4, color);
@@ -288,8 +276,7 @@ void Ending::iniPics()
} }
// Inicializa las escenas // Inicializa las escenas
void Ending::iniScenes() void Ending::iniScenes() {
{
// Variable para los tiempos // Variable para los tiempos
int trigger; int trigger;
constexpr int LAPSE = 80; constexpr int LAPSE = 80;
@@ -371,12 +358,10 @@ void Ending::iniScenes()
} }
// Bucle principal // Bucle principal
void Ending::run() void Ending::run() {
{
JA_PlayMusic(Resource::get()->getMusic("ending1.ogg")); JA_PlayMusic(Resource::get()->getMusic("ending1.ogg"));
while (options.section.section == Section::ENDING) while (options.section.section == Section::ENDING) {
{
update(); update();
checkEvents(); checkEvents();
render(); render();
@@ -387,40 +372,28 @@ void Ending::run()
} }
// Actualiza los contadores // Actualiza los contadores
void Ending::updateCounters() void Ending::updateCounters() {
{
// Incrementa el contador // Incrementa el contador
if (pre_counter_ < 200) if (pre_counter_ < 200) {
{
pre_counter_++; pre_counter_++;
} } else {
else
{
counter_++; counter_++;
} }
if (counter_ > scenes_[current_scene_].counter_end - 100) if (counter_ > scenes_[current_scene_].counter_end - 100) {
{
cover_counter_++; cover_counter_++;
} }
} }
// Actualiza las cortinillas de los elementos // Actualiza las cortinillas de los elementos
void Ending::updateSpriteCovers() void Ending::updateSpriteCovers() {
{
// Actualiza la cortinilla de los textos // Actualiza la cortinilla de los textos
if (counter_ % 4 == 0) if (counter_ % 4 == 0) {
{ for (auto ti : scenes_.at(current_scene_).text_index) {
for (auto ti : scenes_.at(current_scene_).text_index) if (counter_ > ti.trigger) {
{ if (sprite_texts_.at(ti.index).cover_clip_desp > 0) {
if (counter_ > ti.trigger)
{
if (sprite_texts_.at(ti.index).cover_clip_desp > 0)
{
sprite_texts_.at(ti.index).cover_clip_desp -= 2; sprite_texts_.at(ti.index).cover_clip_desp -= 2;
} } else if (sprite_texts_.at(ti.index).cover_clip_height > 0) {
else if (sprite_texts_.at(ti.index).cover_clip_height > 0)
{
sprite_texts_.at(ti.index).cover_clip_height -= 2; sprite_texts_.at(ti.index).cover_clip_height -= 2;
sprite_texts_.at(ti.index).cover_sprite->setY(sprite_texts_.at(ti.index).cover_sprite->getY() + 2); sprite_texts_.at(ti.index).cover_sprite->setY(sprite_texts_.at(ti.index).cover_sprite->getY() + 2);
} }
@@ -430,17 +403,12 @@ void Ending::updateSpriteCovers()
} }
// Actualiza la cortinilla de las imágenes // Actualiza la cortinilla de las imágenes
if (counter_ % 2 == 0) if (counter_ % 2 == 0) {
{ if (sprite_pics_.at(current_scene_).cover_clip_desp > 0) {
if (sprite_pics_.at(current_scene_).cover_clip_desp > 0)
{
sprite_pics_.at(current_scene_).cover_clip_desp -= 2; sprite_pics_.at(current_scene_).cover_clip_desp -= 2;
} } else if (sprite_pics_.at(current_scene_).cover_clip_height > 0) {
else if (sprite_pics_.at(current_scene_).cover_clip_height > 0)
{
sprite_pics_.at(current_scene_).cover_clip_height -= 2; sprite_pics_.at(current_scene_).cover_clip_height -= 2;
if (sprite_pics_.at(current_scene_).cover_clip_height < 0) if (sprite_pics_.at(current_scene_).cover_clip_height < 0) {
{
sprite_pics_.at(current_scene_).cover_clip_height = 0; sprite_pics_.at(current_scene_).cover_clip_height = 0;
} }
sprite_pics_.at(current_scene_).cover_sprite->setY(sprite_pics_.at(current_scene_).cover_sprite->getY() + 2); sprite_pics_.at(current_scene_).cover_sprite->setY(sprite_pics_.at(current_scene_).cover_sprite->getY() + 2);
@@ -450,15 +418,12 @@ void Ending::updateSpriteCovers()
} }
// Comprueba si se ha de cambiar de escena // Comprueba si se ha de cambiar de escena
void Ending::checkChangeScene() void Ending::checkChangeScene() {
{ if (counter_ > scenes_[current_scene_].counter_end) {
if (counter_ > scenes_[current_scene_].counter_end)
{
current_scene_++; current_scene_++;
counter_ = 0; counter_ = 0;
cover_counter_ = 0; cover_counter_ = 0;
if (current_scene_ == 5) if (current_scene_ == 5) {
{
// Termina el bucle // Termina el bucle
options.section.section = Section::ENDING2; options.section.section = Section::ENDING2;
@@ -470,8 +435,7 @@ void Ending::checkChangeScene()
} }
// Rellena la textura para la cortinilla // Rellena la textura para la cortinilla
void Ending::fillCoverTexture() void Ending::fillCoverTexture() {
{
// Rellena la textura que cubre el texto con color transparente // Rellena la textura que cubre el texto con color transparente
auto previuos_renderer = Screen::get()->getRendererSurface(); auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(cover_surface_); Screen::get()->setRendererSurface(cover_surface_);
@@ -480,9 +444,7 @@ void Ending::fillCoverTexture()
// Los primeros 8 pixels crea una malla // Los primeros 8 pixels crea una malla
const Uint8 color = static_cast<Uint8>(PaletteColor::BLACK); const Uint8 color = static_cast<Uint8>(PaletteColor::BLACK);
auto surface = Screen::get()->getRendererSurface(); auto surface = Screen::get()->getRendererSurface();
for (int i = 0; i < 256; i += 2) for (int i = 0; i < 256; i += 2) {
{
surface->putPixel(i + 0, options.game.height + 0, color); surface->putPixel(i + 0, options.game.height + 0, color);
surface->putPixel(i + 1, options.game.height + 1, color); surface->putPixel(i + 1, options.game.height + 1, color);
surface->putPixel(i + 0, options.game.height + 2, color); surface->putPixel(i + 0, options.game.height + 2, color);
@@ -500,10 +462,8 @@ void Ending::fillCoverTexture()
} }
// Dibuja la cortinilla de cambio de escena // Dibuja la cortinilla de cambio de escena
void Ending::renderCoverTexture() void Ending::renderCoverTexture() {
{ if (cover_counter_ > 0) {
if (cover_counter_ > 0)
{
// Dibuja la textura que cubre el texto // Dibuja la textura que cubre el texto
const int OFFSET = std::min(cover_counter_, 100); const int OFFSET = std::min(cover_counter_, 100);
SDL_Rect srcRect = {0, 200 - (cover_counter_ * 2), 256, OFFSET * 2}; SDL_Rect srcRect = {0, 200 - (cover_counter_ * 2), 256, OFFSET * 2};
@@ -513,10 +473,8 @@ void Ending::renderCoverTexture()
} }
// Actualiza el volumen de la musica // Actualiza el volumen de la musica
void Ending::updateMusicVolume() void Ending::updateMusicVolume() {
{ if (current_scene_ == 4 && cover_counter_ > 0) {
if (current_scene_ == 4 && cover_counter_ > 0)
{
const float step = (100.0f - cover_counter_) / 100.0f; const float step = (100.0f - cover_counter_) / 100.0f;
const int volume = 128 * step; const int volume = 128 * step;
JA_SetVolume(volume); JA_SetVolume(volume);

103
source/sections/ending.h Normal file
View File

@@ -0,0 +1,103 @@
#pragma once
#include <SDL3/SDL_stdinc.h> // Para Uint32
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
class SSprite; // lines 8-8
class Surface; // lines 9-9
class Ending {
private:
// Estructuras
struct EndingSurface // Estructura con dos texturas y sprites, uno para mostrar y el otro hace de cortinilla
{
std::shared_ptr<Surface> image_surface; // Surface a mostrar
std::shared_ptr<SSprite> image_sprite; // SSprite para mostrar la textura
std::shared_ptr<Surface> cover_surface; // Surface que cubre a la otra textura
std::shared_ptr<SSprite> cover_sprite; // SSprite para mostrar la textura que cubre a la otra textura
int cover_clip_desp; // Desplazamiento del spriteClip de la textura de cobertura
int cover_clip_height; // Altura del spriteClip de la textura de cobertura
};
struct TextAndPosition // Estructura con un texto y su posición en el eje Y
{
std::string caption; // Texto
int pos; // Posición
};
struct TextIndex {
int index;
int trigger;
};
struct SceneData // Estructura para crear cada una de las escenas del final
{
std::vector<TextIndex> text_index; // Indices del vector de textos a mostrar y su disparador
int picture_index; // Indice del vector de imagenes a mostrar
int counter_end; // Valor del contador en el que finaliza la escena
};
// Objetos y punteros
std::shared_ptr<Surface> cover_surface_; // Surface para cubrir el texto
// Variables
int counter_; // Contador
int pre_counter_; // Contador previo
int cover_counter_; // Contador para la cortinilla
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa
std::vector<EndingSurface> sprite_texts_; // Vector con los sprites de texto con su cortinilla
std::vector<EndingSurface> sprite_pics_; // Vector con los sprites de texto con su cortinilla
int current_scene_; // Escena actual
std::vector<SceneData> scenes_; // Vector con los textos e imagenes de cada escena
// Actualiza el objeto
void update();
// Dibuja el final en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Inicializa los textos
void iniTexts();
// Inicializa las imagenes
void iniPics();
// Inicializa las escenas
void iniScenes();
// Actualiza los contadores
void updateCounters();
// Actualiza las cortinillas de los elementos
void updateSpriteCovers();
// Comprueba si se ha de cambiar de escena
void checkChangeScene();
// Rellena la textura para la cortinilla
void fillCoverTexture();
// Dibuja la cortinilla de cambio de escena
void renderCoverTexture();
// Actualiza el volumen de la musica
void updateMusicVolume();
public:
// Constructor
Ending();
// Destructor
~Ending() = default;
// Bucle principal
void run();
};

View File

@@ -1,8 +1,11 @@
#include "ending2.h" #include "ending2.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_rect.h> // Para SDL_Rect #include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para max, replace #include <algorithm> // Para max, replace
#include "defines.h" // Para GAMECANVAS_CENTER_X, GAMECANVAS_CENTER_Y #include "defines.h" // Para GAMECANVAS_CENTER_X, GAMECANVAS_CENTER_Y
#include "global_events.h" // Para check #include "global_events.h" // Para check
#include "global_inputs.h" // Para check #include "global_inputs.h" // Para check
@@ -17,15 +20,14 @@
#include "utils.h" // Para PaletteColor, stringToColor #include "utils.h" // Para PaletteColor, stringToColor
// Constructor // Constructor
Ending2::Ending2() : state_(EndingState::PRE_CREDITS, SDL_GetTicks(), STATE_PRE_CREDITS_DURATION_) Ending2::Ending2()
{ : state_(EndingState::PRE_CREDITS, SDL_GetTicks(), STATE_PRE_CREDITS_DURATION_) {
options.section.section = Section::ENDING2; options.section.section = Section::ENDING2;
options.section.subsection = Subsection::NONE; options.section.subsection = Subsection::NONE;
// Inicializa el vector de colores // Inicializa el vector de colores
const std::vector<std::string> COLORS = {"white", "yellow", "cyan", "green", "magenta", "red", "blue", "black"}; const std::vector<std::string> COLORS = {"white", "yellow", "cyan", "green", "magenta", "red", "blue", "black"};
for (const auto &color : COLORS) for (const auto& color : COLORS) {
{
colors_.push_back(stringToColor(color)); colors_.push_back(stringToColor(color));
} }
@@ -49,11 +51,9 @@ Ending2::Ending2() : state_(EndingState::PRE_CREDITS, SDL_GetTicks(), STATE_PRE_
} }
// Actualiza el objeto // Actualiza el objeto
void Ending2::update() void Ending2::update() {
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
{
// Actualiza el contador de ticks // Actualiza el contador de ticks
ticks_ = SDL_GetTicks(); ticks_ = SDL_GetTicks();
@@ -63,12 +63,10 @@ void Ending2::update()
// Actualiza el estado // Actualiza el estado
updateState(); updateState();
switch (state_.state) switch (state_.state) {
{
case EndingState::CREDITS: case EndingState::CREDITS:
// Actualiza los sprites, los textos y los textos del final // Actualiza los sprites, los textos y los textos del final
for (int i = 0; i < 25; ++i) for (int i = 0; i < 25; ++i) {
{
updateSprites(); updateSprites();
updateTextSprites(); updateTextSprites();
updateTexts(); updateTexts();
@@ -92,8 +90,7 @@ void Ending2::update()
} }
// Dibuja el final en pantalla // Dibuja el final en pantalla
void Ending2::render() void Ending2::render() {
{
// Prepara para empezar a dibujar en la surface de juego // Prepara para empezar a dibujar en la surface de juego
Screen::get()->start(); Screen::get()->start();
@@ -112,8 +109,7 @@ void Ending2::render()
// Dibuja una trama arriba y abajo // Dibuja una trama arriba y abajo
Uint8 color = static_cast<Uint8>(PaletteColor::BLACK); Uint8 color = static_cast<Uint8>(PaletteColor::BLACK);
auto surface = Screen::get()->getRendererSurface(); auto surface = Screen::get()->getRendererSurface();
for (int i = 0; i < 256; i += 2) for (int i = 0; i < 256; i += 2) {
{
surface->putPixel(i + 0, 0, color); surface->putPixel(i + 0, 0, color);
surface->putPixel(i + 1, 1, color); surface->putPixel(i + 1, 1, color);
surface->putPixel(i + 0, 2, color); surface->putPixel(i + 0, 2, color);
@@ -136,28 +132,23 @@ void Ending2::render()
} }
// Comprueba el manejador de eventos // Comprueba el manejador de eventos
void Ending2::checkEvents() void Ending2::checkEvents() {
{
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event)) {
{
globalEvents::check(event); globalEvents::check(event);
} }
} }
// Comprueba las entradas // Comprueba las entradas
void Ending2::checkInput() void Ending2::checkInput() {
{
globalInputs::check(); globalInputs::check();
} }
// Bucle principal // Bucle principal
void Ending2::run() void Ending2::run() {
{
JA_PlayMusic(Resource::get()->getMusic("ending2.ogg")); JA_PlayMusic(Resource::get()->getMusic("ending2.ogg"));
while (options.section.section == Section::ENDING2) while (options.section.section == Section::ENDING2) {
{
update(); update();
checkEvents(); checkEvents();
render(); render();
@@ -168,34 +159,28 @@ void Ending2::run()
} }
// Actualiza el estado // Actualiza el estado
void Ending2::updateState() void Ending2::updateState() {
{ switch (state_.state) {
switch (state_.state)
{
case EndingState::PRE_CREDITS: case EndingState::PRE_CREDITS:
if (state_.hasEnded(EndingState::PRE_CREDITS)) if (state_.hasEnded(EndingState::PRE_CREDITS)) {
{
state_.set(EndingState::CREDITS, 0); state_.set(EndingState::CREDITS, 0);
} }
break; break;
case EndingState::CREDITS: case EndingState::CREDITS:
if (texts_.back()->getPosY() <= GAMECANVAS_CENTER_Y) if (texts_.back()->getPosY() <= GAMECANVAS_CENTER_Y) {
{
state_.set(EndingState::POST_CREDITS, STATE_POST_CREDITS_DURATION_); state_.set(EndingState::POST_CREDITS, STATE_POST_CREDITS_DURATION_);
} }
break; break;
case EndingState::POST_CREDITS: case EndingState::POST_CREDITS:
if (state_.hasEnded(EndingState::POST_CREDITS)) if (state_.hasEnded(EndingState::POST_CREDITS)) {
{
state_.set(EndingState::FADING, STATE_FADE_DURATION_); state_.set(EndingState::FADING, STATE_FADE_DURATION_);
} }
break; break;
case EndingState::FADING: case EndingState::FADING:
if (state_.hasEnded(EndingState::FADING)) if (state_.hasEnded(EndingState::FADING)) {
{
options.section.section = Section::LOGO; options.section.section = Section::LOGO;
options.section.subsection = Subsection::LOGO_TO_INTRO; options.section.subsection = Subsection::LOGO_TO_INTRO;
} }
@@ -207,8 +192,7 @@ void Ending2::updateState()
} }
// Inicializa la lista de sprites // Inicializa la lista de sprites
void Ending2::iniSpriteList() void Ending2::iniSpriteList() {
{
// Reinicia el vector // Reinicia el vector
sprite_list_.clear(); sprite_list_.clear();
@@ -285,15 +269,13 @@ void Ending2::iniSpriteList()
} }
// Carga todos los sprites desde una lista // Carga todos los sprites desde una lista
void Ending2::loadSprites() void Ending2::loadSprites() {
{
// Inicializa variables // Inicializa variables
sprite_max_width_ = 0; sprite_max_width_ = 0;
sprite_max_height_ = 0; sprite_max_height_ = 0;
// Carga los sprites // Carga los sprites
for (const auto &file : sprite_list_) for (const auto& file : sprite_list_) {
{
sprites_.emplace_back(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface(file + ".gif"), Resource::get()->getAnimations(file + ".ani"))); sprites_.emplace_back(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface(file + ".gif"), Resource::get()->getAnimations(file + ".ani")));
sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_); sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_);
sprite_max_height_ = std::max(sprites_.back()->getHeight(), sprite_max_height_); sprite_max_height_ = std::max(sprites_.back()->getHeight(), sprite_max_height_);
@@ -301,42 +283,33 @@ void Ending2::loadSprites()
} }
// Actualiza los sprites // Actualiza los sprites
void Ending2::updateSprites() void Ending2::updateSprites() {
{ for (auto sprite : sprites_) {
for (auto sprite : sprites_)
{
sprite->update(); sprite->update();
} }
} }
// Actualiza los sprites de texto // Actualiza los sprites de texto
void Ending2::updateTextSprites() void Ending2::updateTextSprites() {
{ for (auto sprite : sprite_texts_) {
for (auto sprite : sprite_texts_)
{
sprite->update(); sprite->update();
} }
} }
// Actualiza los sprites de texto del final // Actualiza los sprites de texto del final
void Ending2::updateTexts() void Ending2::updateTexts() {
{ for (auto sprite : texts_) {
for (auto sprite : texts_)
{
sprite->update(); sprite->update();
} }
} }
// Dibuja los sprites // Dibuja los sprites
void Ending2::renderSprites() void Ending2::renderSprites() {
{
const Uint8 colorA = static_cast<Uint8>(PaletteColor::RED); const Uint8 colorA = static_cast<Uint8>(PaletteColor::RED);
for (auto sprite : sprites_) for (auto sprite : sprites_) {
{
const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool A = sprite->getRect().y + sprite->getRect().h > 0;
const bool B = sprite->getRect().y < options.game.height; const bool B = sprite->getRect().y < options.game.height;
if (A && B) if (A && B) {
{
sprite->render(1, colorA); sprite->render(1, colorA);
} }
} }
@@ -347,39 +320,31 @@ void Ending2::renderSprites()
} }
// Dibuja los sprites con el texto // Dibuja los sprites con el texto
void Ending2::renderSpriteTexts() void Ending2::renderSpriteTexts() {
{
const Uint8 color = static_cast<Uint8>(PaletteColor::WHITE); const Uint8 color = static_cast<Uint8>(PaletteColor::WHITE);
for (auto sprite : sprite_texts_) for (auto sprite : sprite_texts_) {
{
const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool A = sprite->getRect().y + sprite->getRect().h > 0;
const bool B = sprite->getRect().y < options.game.height; const bool B = sprite->getRect().y < options.game.height;
if (A && B) if (A && B) {
{
sprite->render(1, color); sprite->render(1, color);
} }
} }
} }
// Dibuja los sprites con el texto del final // Dibuja los sprites con el texto del final
void Ending2::renderTexts() void Ending2::renderTexts() {
{ for (auto sprite : texts_) {
for (auto sprite : texts_)
{
const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool A = sprite->getRect().y + sprite->getRect().h > 0;
const bool B = sprite->getRect().y < options.game.height; const bool B = sprite->getRect().y < options.game.height;
if (A && B) if (A && B) {
{
sprite->render(); sprite->render();
} }
} }
} }
// Coloca los sprites en su sito // Coloca los sprites en su sito
void Ending2::placeSprites() void Ending2::placeSprites() {
{ for (int i = 0; i < static_cast<int>(sprites_.size()); ++i) {
for (int i = 0; i < static_cast<int>(sprites_.size()); ++i)
{
const int X = i % 2 == 0 ? FIRST_COL_ : SECOND_COL_; const int X = i % 2 == 0 ? FIRST_COL_ : SECOND_COL_;
const int Y = (i / 1) * (sprite_max_height_ + DIST_SPRITE_TEXT_ + Resource::get()->getText("smb2")->getCharacterSize() + DIST_SPRITE_SPRITE_) + options.game.height + 40; const int Y = (i / 1) * (sprite_max_height_ + DIST_SPRITE_TEXT_ + Resource::get()->getText("smb2")->getCharacterSize() + DIST_SPRITE_SPRITE_) + options.game.height + 40;
const int W = sprites_.at(i)->getWidth(); const int W = sprites_.at(i)->getWidth();
@@ -399,18 +364,15 @@ void Ending2::placeSprites()
} }
// Crea los sprites con las texturas con los textos // Crea los sprites con las texturas con los textos
void Ending2::createSpriteTexts() void Ending2::createSpriteTexts() {
{
// Crea los sprites de texto a partir de la lista // Crea los sprites de texto a partir de la lista
for (int i = 0; i < static_cast<int>(sprite_list_.size()); ++i) for (int i = 0; i < static_cast<int>(sprite_list_.size()); ++i) {
{
auto text = Resource::get()->getText("smb2"); auto text = Resource::get()->getText("smb2");
// Procesa y ajusta el texto del sprite actual // Procesa y ajusta el texto del sprite actual
std::string txt = sprite_list_[i]; std::string txt = sprite_list_[i];
std::replace(txt.begin(), txt.end(), '_', ' '); // Reemplaza '_' por ' ' std::replace(txt.begin(), txt.end(), '_', ' '); // Reemplaza '_' por ' '
if (txt == "player") if (txt == "player") {
{
txt = "JAILDOCTOR"; // Reemplaza "player" por "JAILDOCTOR" txt = "JAILDOCTOR"; // Reemplaza "player" por "JAILDOCTOR"
} }
@@ -441,8 +403,7 @@ void Ending2::createSpriteTexts()
} }
// Crea los sprites con las texturas con los textos del final // Crea los sprites con las texturas con los textos del final
void Ending2::createTexts() void Ending2::createTexts() {
{
// Crea los primeros textos // Crea los primeros textos
std::vector<std::string> list; std::vector<std::string> list;
list.push_back("STARRING"); list.push_back("STARRING");
@@ -450,8 +411,7 @@ void Ending2::createTexts()
auto text = Resource::get()->getText("smb2"); auto text = Resource::get()->getText("smb2");
// Crea los sprites de texto a partir de la lista // Crea los sprites de texto a partir de la lista
for (int i = 0; i < (int)list.size(); ++i) for (int i = 0; i < (int)list.size(); ++i) {
{
// Calcula constantes // Calcula constantes
const int w = text->lenght(list[i], 1); const int w = text->lenght(list[i], 1);
const int h = text->getCharacterSize(); const int h = text->getCharacterSize();
@@ -480,8 +440,7 @@ void Ending2::createTexts()
list.push_back("FOR PLAYING!"); list.push_back("FOR PLAYING!");
// Crea los sprites de texto a partir de la lista // Crea los sprites de texto a partir de la lista
for (int i = 0; i < (int)list.size(); ++i) for (int i = 0; i < (int)list.size(); ++i) {
{
// Calcula constantes // Calcula constantes
const int w = text->lenght(list[i], 1); const int w = text->lenght(list[i], 1);
const int h = text->getCharacterSize(); const int h = text->getCharacterSize();
@@ -504,17 +463,14 @@ void Ending2::createTexts()
} }
// Actualiza el fade final // Actualiza el fade final
void Ending2::updateFinalFade() void Ending2::updateFinalFade() {
{ for (auto sprite : texts_) {
for (auto sprite : texts_)
{
sprite->getSurface()->fadeSubPalette(0); sprite->getSurface()->fadeSubPalette(0);
} }
} }
// Actualiza el volumen de la musica // Actualiza el volumen de la musica
void Ending2::updateMusicVolume() void Ending2::updateMusicVolume() {
{
// Constante para la duración en milisegundos // Constante para la duración en milisegundos
constexpr Uint32 VOLUME_FADE_DURATION = 3000; constexpr Uint32 VOLUME_FADE_DURATION = 3000;

140
source/sections/ending2.h Normal file
View File

@@ -0,0 +1,140 @@
#pragma once
#include <SDL3/SDL_stdinc.h> // Para Uint32, Uint8
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "defines.h" // Para GAMECANVAS_WIDTH, GAMECANVAS_FIRST_QUAR...
class SAnimatedSprite; // lines 9-9
class SMovingSprite; // lines 10-10
class Ending2 {
private:
// Enum para representar los estados del final
enum class EndingState : int {
PRE_CREDITS, // Estado previo a los créditos
CREDITS, // Estado de los créditos
POST_CREDITS, // Estado posterior a los créditos
FADING, // Estado de fundido de los textos a negrp
};
// Estructura para controlar los estados y su duración
struct State {
EndingState state; // Estado actual
Uint32 init_ticks; // Ticks en los que se inicializó el estado
Uint32 duration; // Duración en milisegundos para el estado actual
// Constructor parametrizado para inicializar la estructura
State(EndingState initialState, Uint32 initialTicks, Uint32 stateDuration)
: state(initialState),
init_ticks(initialTicks),
duration(stateDuration) {}
// Método para comprobar si el estado ha terminado y verifica el nombre del estado
bool hasEnded(EndingState expectedState) const {
// Comprobar si el estado actual coincide con el estado esperado
if (state != expectedState) {
return false; // Si no coincide, considerar que no ha terminado
}
// Comprobar si el tiempo transcurrido excede la duración
return (SDL_GetTicks() - init_ticks) >= duration;
}
// Método para establecer un nuevo estado
void set(EndingState newState, Uint32 newDuration) {
state = newState; // Actualizar el estado
init_ticks = SDL_GetTicks(); // Reiniciar el tiempo de inicio
duration = newDuration; // Actualizar la duración
}
};
// Constantes
static constexpr int FIRST_COL_ = GAMECANVAS_FIRST_QUARTER_X + (GAMECANVAS_WIDTH / 16); // Primera columna por donde desfilan los sprites
static constexpr int SECOND_COL_ = GAMECANVAS_THIRD_QUARTER_X - (GAMECANVAS_WIDTH / 16); // Segunda columna por donde desfilan los sprites
static constexpr int DIST_SPRITE_TEXT_ = 8; // Distancia entre el sprite y el texto que lo acompaña
static constexpr int DIST_SPRITE_SPRITE_ = 0; // Distancia entre dos sprites de la misma columna
static constexpr float SPRITE_DESP_SPEED_ = -0.2f; // Velocidad de desplazamiento de los sprites
static constexpr int STATE_PRE_CREDITS_DURATION_ = 3000;
static constexpr int STATE_POST_CREDITS_DURATION_ = 5000;
static constexpr int STATE_FADE_DURATION_ = 5000;
// Objetos y punteros
std::vector<std::shared_ptr<SAnimatedSprite>> sprites_; // Vector con todos los sprites a dibujar
std::vector<std::shared_ptr<SMovingSprite>> sprite_texts_; // Vector con los sprites de texto de los sprites
std::vector<std::shared_ptr<SMovingSprite>> texts_; // Vector con los sprites de texto
// Variables
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<std::string> sprite_list_; // Lista con todos los sprites a dibujar
std::vector<Uint8> colors_; // Vector con los colores para el fade
int sprite_max_width_ = 0; // El valor de ancho del sprite mas ancho
int sprite_max_height_ = 0; // El valor de alto del sprite mas alto
State state_; // Controla el estado de la clase
// Actualiza el objeto
void update();
// Dibuja el final en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Actualiza el estado
void updateState();
// Inicializa la lista de sprites
void iniSpriteList();
// Carga todos los sprites desde una lista
void loadSprites();
// Actualiza los sprites
void updateSprites();
// Actualiza los sprites de texto
void updateTextSprites();
// Actualiza los sprites de texto del final
void updateTexts();
// Dibuja los sprites
void renderSprites();
// Dibuja los sprites con el texto
void renderSpriteTexts();
// Dibuja los sprites con el texto del final
void renderTexts();
// Coloca los sprites en su sito
void placeSprites();
// Crea los sprites con las texturas con los textos
void createSpriteTexts();
// Crea los sprites con las texturas con los textos del final
void createTexts();
// Actualiza el fade final
void updateFinalFade();
// Actualiza el volumen de la musica
void updateMusicVolume();
public:
// Constructor
Ending2();
// Destructor
~Ending2() = default;
// Bucle principal
void run();
};

622
source/sections/game.cpp Normal file
View File

@@ -0,0 +1,622 @@
#include "game.h"
#include <SDL3/SDL_render.h> // Para SDL_FLIP_HORIZONTAL
#include <SDL3/SDL_scancode.h> // Para SDL_SCANCODE_7, SDL_SCANCODE_A, SDL_S...
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <vector> // Para vector
#include "asset.h" // Para Asset
#include "cheevos.h" // Para Cheevos
#include "debug.h" // Para Debug
#include "defines.h" // Para BLOCK, PLAY_AREA_HEIGHT, RoomBorder::BOTTOM
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, InputAction, INPUT_DO_NOT_ALLOW_REPEAT
#include "item_tracker.h" // Para ItemTracker
#include "jail_audio.h" // Para JA_PauseMusic, JA_GetMusicState, JA_P...
#include "notifier.h" // Para Notifier, NotificationText, CHEEVO_NO...
#include "options.h" // Para Options, options, Cheat, SectionState
#include "resource.h" // Para ResourceRoom, Resource
#include "room.h" // Para Room, RoomData
#include "room_tracker.h" // Para RoomTracker
#include "scoreboard.h" // Para ScoreboardData, Scoreboard
#include "screen.h" // Para Screen
#include "stats.h" // Para Stats
#include "surface.h" // Para Surface
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "utils.h" // Para PaletteColor, stringToColor
// Constructor
Game::Game(GameMode mode)
: board_(std::make_shared<ScoreboardData>(0, 9, 0, true, 0, SDL_GetTicks(), options.cheats.jail_is_open == Cheat::CheatState::ENABLED)),
scoreboard_(std::make_shared<Scoreboard>(board_)),
room_tracker_(std::make_shared<RoomTracker>()),
stats_(std::make_shared<Stats>(Asset::get()->get("stats.csv"), Asset::get()->get("stats_buffer.csv"))),
mode_(mode),
#ifdef DEBUG
current_room_("03.room"),
spawn_point_(PlayerSpawn(25 * BLOCK, 13 * BLOCK, 0, 0, 0, PlayerState::STANDING, SDL_FLIP_HORIZONTAL))
#else
current_room_("03.room"),
spawn_point_(PlayerSpawn(25 * BLOCK, 13 * BLOCK, 0, 0, 0, PlayerState::STANDING, SDL_FLIP_HORIZONTAL))
#endif
{
#ifdef DEBUG
Debug::get()->setEnabled(false);
#endif
// Crea objetos e inicializa variables
ItemTracker::init();
DEMO_init();
room_ = std::make_shared<Room>(current_room_, board_);
initPlayer(spawn_point_, room_);
initStats();
total_items_ = getTotalItems();
createRoomNameTexture();
changeRoom(current_room_);
Cheevos::get()->enable(!options.cheats.enabled()); // Deshabilita los logros si hay trucos activados
Cheevos::get()->clearUnobtainableState();
options.section.section = (mode_ == GameMode::GAME) ? Section::GAME : Section::DEMO;
options.section.subsection = Subsection::NONE;
}
Game::~Game() {
ItemTracker::destroy();
}
// Comprueba los eventos de la cola
void Game::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
globalEvents::check(event);
#ifdef DEBUG
checkDebugEvents(event);
#endif
}
}
// Comprueba el teclado
void Game::checkInput() {
if (Input::get()->checkInput(InputAction::TOGGLE_MUSIC, INPUT_DO_NOT_ALLOW_REPEAT)) {
board_->music = !board_->music;
board_->music ? JA_ResumeMusic() : JA_PauseMusic();
Notifier::get()->show({"MUSIC " + std::string(board_->music ? "ENABLED" : "DISABLED")}, NotificationText::CENTER);
}
else if (Input::get()->checkInput(InputAction::PAUSE, INPUT_DO_NOT_ALLOW_REPEAT)) {
togglePause();
Notifier::get()->show({std::string(paused_ ? "GAME PAUSED" : "GAME RUNNING")}, NotificationText::CENTER);
}
globalInputs::check();
}
// Bucle para el juego
void Game::run() {
keepMusicPlaying();
if (!board_->music && mode_ == GameMode::GAME) {
JA_PauseMusic();
}
while (options.section.section == Section::GAME || options.section.section == Section::DEMO) {
update();
checkEvents();
render();
}
if (mode_ == GameMode::GAME) {
JA_StopMusic();
}
}
// Actualiza el juego, las variables, comprueba la entrada, etc.
void Game::update() {
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba el teclado
checkInput();
#ifdef DEBUG
Debug::get()->clear();
#endif
// Actualiza los objetos
room_->update();
if (mode_ == GameMode::GAME) {
player_->update();
checkPlayerIsOnBorder();
checkPlayerAndItems();
checkPlayerAndEnemies();
checkIfPlayerIsAlive();
checkGameOver();
checkEndGame();
checkRestoringJail();
checkSomeCheevos();
}
DEMO_checkRoomChange();
scoreboard_->update();
keepMusicPlaying();
updateBlackScreen();
Screen::get()->update();
#ifdef DEBUG
updateDebugInfo();
#endif
}
}
// Pinta los objetos en pantalla
void Game::render() {
// Prepara para dibujar el frame
Screen::get()->start();
// Dibuja los elementos del juego en orden
room_->renderMap();
room_->renderEnemies();
room_->renderItems();
if (mode_ == GameMode::GAME) {
player_->render();
}
renderRoomName();
scoreboard_->render();
renderBlackScreen();
#ifdef DEBUG
// Debug info
renderDebugInfo();
#endif
// Actualiza la pantalla
Screen::get()->render();
}
#ifdef DEBUG
// Pasa la información de debug
void Game::updateDebugInfo() {
Debug::get()->add("X = " + std::to_string(static_cast<int>(player_->x_)) + ", Y = " + std::to_string(static_cast<int>(player_->y_)));
Debug::get()->add("VX = " + std::to_string(player_->vx_).substr(0, 4) + ", VY = " + std::to_string(player_->vy_).substr(0, 4));
Debug::get()->add("STATE = " + std::to_string(static_cast<int>(player_->state_)));
}
// Pone la información de debug en pantalla
void Game::renderDebugInfo() {
if (!Debug::get()->getEnabled()) {
return;
}
auto surface = Screen::get()->getRendererSurface();
// Borra el marcador
SDL_Rect rect = {0, 18 * BLOCK, PLAY_AREA_WIDTH, GAMECANVAS_HEIGHT - PLAY_AREA_HEIGHT};
surface->fillRect(&rect, static_cast<Uint8>(PaletteColor::BLACK));
// Pinta la rejilla
/*for (int i = 0; i < PLAY_AREA_BOTTOM; i += 8)
{
// Lineas horizontales
surface->drawLine(0, i, PLAY_AREA_RIGHT, i, static_cast<Uint8>(PaletteColor::BRIGHT_BLACK));
}
for (int i = 0; i < PLAY_AREA_RIGHT; i += 8)
{
// Lineas verticales
surface->drawLine(i, 0, i, PLAY_AREA_BOTTOM - 1, static_cast<Uint8>(PaletteColor::BRIGHT_BLACK));
}*/
// Pinta el texto
Debug::get()->setPos({1, 18 * 8});
Debug::get()->render();
}
// Comprueba los eventos
void Game::checkDebugEvents(const SDL_Event& event) {
if (event.type == SDL_KEYDOWN && event.key.repeat == 0) {
switch (event.key.keysym.scancode) {
case SDL_SCANCODE_G:
Debug::get()->toggleEnabled();
options.cheats.invincible = static_cast<Cheat::CheatState>(Debug::get()->getEnabled());
board_->music = !Debug::get()->getEnabled();
board_->music ? JA_ResumeMusic() : JA_PauseMusic();
break;
case SDL_SCANCODE_R:
Resource::get()->reload();
break;
case SDL_SCANCODE_W:
changeRoom(room_->getRoom(RoomBorder::TOP));
break;
case SDL_SCANCODE_A:
changeRoom(room_->getRoom(RoomBorder::LEFT));
break;
case SDL_SCANCODE_S:
changeRoom(room_->getRoom(RoomBorder::BOTTOM));
break;
case SDL_SCANCODE_D:
changeRoom(room_->getRoom(RoomBorder::RIGHT));
break;
case SDL_SCANCODE_7:
Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", "I LIKE MY MULTICOLOURED FRIENDS"}, NotificationText::CENTER, CHEEVO_NOTIFICATION_DURATION, -1, false, "F7");
break;
default:
break;
}
}
}
#endif
// Escribe el nombre de la pantalla
void Game::renderRoomName() {
// Dibuja la textura con el nombre de la habitación
room_name_surface_->render(nullptr, &room_name_rect_);
}
// Cambia de habitación
bool Game::changeRoom(const std::string& room_path) {
// En las habitaciones los limites tienen la cadena del fichero o un 0 en caso de no limitar con nada
if (room_path == "0") {
return false;
}
// Verifica que exista el fichero que se va a cargar
if (Asset::get()->get(room_path) != "") {
// Crea un objeto habitación nuevo a partir del fichero
room_ = std::make_shared<Room>(room_path, board_);
// Pone el nombre de la habitación en la textura
fillRoomNameTexture();
// Pone el color del marcador en función del color del borde de la habitación
setScoreBoardColor();
if (room_tracker_->addRoom(room_path)) {
// Incrementa el contador de habitaciones visitadas
board_->rooms++;
options.stats.rooms = board_->rooms;
// Actualiza las estadisticas
stats_->addVisit(room_->getName());
}
// Pasa la nueva habitación al jugador
player_->setRoom(room_);
// Cambia la habitación actual
current_room_ = room_path;
return true;
}
return false;
}
// Comprueba si el jugador esta en el borde de la pantalla
void Game::checkPlayerIsOnBorder() {
if (player_->getOnBorder()) {
const std::string roomName = room_->getRoom(player_->getBorder());
if (changeRoom(roomName)) {
player_->switchBorders();
spawn_point_ = player_->getSpawnParams();
}
}
}
// Comprueba las colisiones del jugador con los enemigos
bool Game::checkPlayerAndEnemies() {
const bool death = room_->enemyCollision(player_->getCollider());
if (death) {
killPlayer();
}
return death;
}
// Comprueba las colisiones del jugador con los objetos
void Game::checkPlayerAndItems() {
room_->itemCollision(player_->getCollider());
}
// Comprueba si el jugador esta vivo
void Game::checkIfPlayerIsAlive() {
if (!player_->isAlive()) {
killPlayer();
}
}
// Comprueba si ha terminado la partida
void Game::checkGameOver() {
if (board_->lives < 0 && black_screen_counter_ > 17) {
options.section.section = Section::GAME_OVER;
}
}
// Mata al jugador
void Game::killPlayer() {
if (options.cheats.invincible == Cheat::CheatState::ENABLED) {
return;
}
// Resta una vida al jugador
if (options.cheats.infinite_lives == Cheat::CheatState::DISABLED) {
--board_->lives;
}
// Actualiza las estadisticas
stats_->addDeath(room_->getName());
// Invalida el logro de pasarse el juego sin morir
Cheevos::get()->setUnobtainable(11);
// Sonido
JA_PlaySound(Resource::get()->getSound("death.wav"));
// Pone la pantalla en negro un tiempo
setBlackScreen();
// Crea la nueva habitación y el nuevo jugador
room_ = std::make_shared<Room>(current_room_, board_);
initPlayer(spawn_point_, room_);
// Pone los objetos en pausa mientras esta la habitación en negro
room_->setPaused(true);
player_->setPaused(true);
}
// Establece la pantalla en negro
void Game::setBlackScreen() {
black_screen_ = true;
}
// Actualiza las variables relativas a la pantalla en negro
void Game::updateBlackScreen() {
if (black_screen_) {
black_screen_counter_++;
if (black_screen_counter_ > 20) {
black_screen_ = false;
black_screen_counter_ = 0;
player_->setPaused(false);
room_->setPaused(false);
Screen::get()->setBorderColor(room_->getBorderColor());
}
}
}
// Dibuja la pantalla negra
void Game::renderBlackScreen() {
if (black_screen_) {
auto const color = static_cast<Uint8>(PaletteColor::BLACK);
Screen::get()->setRendererSurface();
Screen::get()->clearSurface(color);
Screen::get()->setBorderColor(color);
}
}
// Pone el color del marcador en función del color del borde de la habitación
void Game::setScoreBoardColor() {
// Obtiene el color del borde
const Uint8 BORDER_COLOR = room_->getBorderColor();
const bool IS_BLACK = BORDER_COLOR == static_cast<Uint8>(PaletteColor::BLACK);
const bool IS_BRIGHT_BLACK = BORDER_COLOR == static_cast<Uint8>(PaletteColor::BRIGHT_BLACK);
// Si el color del borde es negro o negro brillante cambia el texto del marcador a blanco
board_->color = IS_BLACK || IS_BRIGHT_BLACK ? static_cast<Uint8>(PaletteColor::WHITE) : BORDER_COLOR;
}
// Comprueba si ha finalizado el juego
bool Game::checkEndGame() {
const bool isOnTheRoom = room_->getName() == "THE JAIL"; // Estar en la habitación que toca
const bool haveTheItems = board_->items >= int(total_items_ * 0.9f) || options.cheats.jail_is_open == Cheat::CheatState::ENABLED; // Con mas del 90% de los items recogidos
const bool isOnTheDoor = player_->getRect().x <= 128; // Y en la ubicación que toca (En la puerta)
if (haveTheItems) {
board_->jail_is_open = true;
}
if (haveTheItems && isOnTheRoom && isOnTheDoor) {
// Comprueba los logros de completar el juego
checkEndGameCheevos();
options.section.section = Section::ENDING;
return true;
}
return false;
}
// Obtiene la cantidad total de items que hay en el mapeado del juego
int Game::getTotalItems() {
int items = 0;
auto rooms = Resource::get()->getRooms();
for (const auto& room : rooms) {
items += room.room->items.size();
}
return items;
}
// Pone el juego en pausa
void Game::togglePause() {
paused_ = !paused_;
player_->setPaused(paused_);
room_->setPaused(paused_);
scoreboard_->setPaused(paused_);
}
// Da vidas al jugador cuando está en la Jail
void Game::checkRestoringJail() {
if (room_->getName() != "THE JAIL" || board_->lives == 9) {
return;
}
static int counter = 0;
if (!paused_) {
counter++;
}
// Incrementa el numero de vidas
if (counter == 100) {
counter = 0;
board_->lives++;
JA_PlaySound(Resource::get()->getSound("death.wav"));
// Invalida el logro de completar el juego sin entrar a la jail
const bool haveTheItems = board_->items >= int(total_items_ * 0.9f);
if (!haveTheItems) {
Cheevos::get()->setUnobtainable(9);
}
}
}
// Inicializa el diccionario de las estadísticas
void Game::initStats() {
auto rooms = Resource::get()->getRooms();
for (const auto& room : rooms) {
stats_->addDictionary(room.room->number, room.room->name);
}
stats_->init();
}
// Crea la textura con el nombre de la habitación
void Game::fillRoomNameTexture() {
// Pone la textura como destino de renderizado
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(room_name_surface_);
// Rellena la textura de color
room_name_surface_->clear(stringToColor("white"));
// Escribe el texto en la textura
auto text = Resource::get()->getText("smb2");
text->writeDX(TEXT_CENTER | TEXT_COLOR, GAMECANVAS_CENTER_X, text->getCharacterSize() / 2, room_->getName(), 1, room_->getBGColor());
// Deja el renderizador por defecto
Screen::get()->setRendererSurface(previuos_renderer);
}
// Comprueba algunos logros
void Game::checkSomeCheevos() {
auto cheevos = Cheevos::get();
// Logros sobre la cantidad de items
if (board_->items == total_items_) {
cheevos->unlock(4);
cheevos->unlock(3);
cheevos->unlock(2);
cheevos->unlock(1);
} else if (board_->items >= total_items_ * 0.75f) {
cheevos->unlock(3);
cheevos->unlock(2);
cheevos->unlock(1);
} else if (board_->items >= total_items_ * 0.5f) {
cheevos->unlock(2);
cheevos->unlock(1);
} else if (board_->items >= total_items_ * 0.25f) {
cheevos->unlock(1);
}
// Logros sobre las habitaciones visitadas
if (board_->rooms >= 60) {
cheevos->unlock(7);
cheevos->unlock(6);
cheevos->unlock(5);
} else if (board_->rooms >= 40) {
cheevos->unlock(6);
cheevos->unlock(5);
} else if (board_->rooms >= 20) {
cheevos->unlock(5);
}
}
// Comprueba los logros de completar el juego
void Game::checkEndGameCheevos() {
auto cheevos = Cheevos::get();
// "Complete the game"
cheevos->unlock(8);
// "Complete the game without entering the jail"
cheevos->unlock(9);
// "Complete the game with all items"
if (board_->items == total_items_) {
cheevos->unlock(10);
}
// "Complete the game without dying"
cheevos->unlock(11);
// "Complete the game in under 30 minutes"
if (scoreboard_->getMinutes() < 30) {
cheevos->unlock(12);
}
}
// Inicializa al jugador
void Game::initPlayer(const PlayerSpawn& spawn_point, std::shared_ptr<Room> room) {
std::string player_texture = options.cheats.alternate_skin == Cheat::CheatState::ENABLED ? "player2.gif" : "player.gif";
std::string player_animations = options.cheats.alternate_skin == Cheat::CheatState::ENABLED ? "player2.ani" : "player.ani";
const PlayerData player(spawn_point, player_texture, player_animations, room);
player_ = std::make_shared<Player>(player);
}
// Crea la textura para poner el nombre de la habitación
void Game::createRoomNameTexture() {
auto text = Resource::get()->getText("smb2");
room_name_surface_ = std::make_shared<Surface>(options.game.width, text->getCharacterSize() * 2);
// Establece el destino de la textura
room_name_rect_ = {0, PLAY_AREA_HEIGHT, options.game.width, text->getCharacterSize() * 2};
}
// Hace sonar la música
void Game::keepMusicPlaying() {
const std::string music_path = mode_ == GameMode::GAME ? "game.ogg" : "title.ogg";
// Si la música no está sonando
if (JA_GetMusicState() == JA_MUSIC_INVALID || JA_GetMusicState() == JA_MUSIC_STOPPED) {
JA_PlayMusic(Resource::get()->getMusic(music_path));
}
}
// DEMO MODE: Inicializa las variables para el modo demo
void Game::DEMO_init() {
if (mode_ == GameMode::DEMO) {
demo_ = DemoData(0, 400, 0, {"04.room", "54.room", "20.room", "09.room", "05.room", "11.room", "31.room", "44.room"});
current_room_ = demo_.rooms.front();
}
}
// DEMO MODE: Comprueba si se ha de cambiar de habitación
void Game::DEMO_checkRoomChange() {
if (mode_ == GameMode::DEMO) {
demo_.counter++;
if (demo_.counter == demo_.room_time) {
demo_.counter = 0;
demo_.room_index++;
if (demo_.room_index == (int)demo_.rooms.size()) {
options.section.section = Section::LOGO;
options.section.subsection = Subsection::LOGO_TO_TITLE;
} else {
changeRoom(demo_.rooms[demo_.room_index]);
}
}
}
}

177
source/sections/game.h Normal file
View File

@@ -0,0 +1,177 @@
#pragma once
#include <SDL3/SDL_events.h> // Para SDL_Event
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_stdinc.h> // Para Uint32
#include <initializer_list> // Para initializer_list
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "player.h" // Para PlayerSpawn
class Room; // lines 12-12
class RoomTracker; // lines 13-13
class Scoreboard; // lines 14-14
class Stats; // lines 15-15
class Surface;
struct ScoreboardData; // lines 16-16
enum class GameMode {
DEMO,
GAME
};
class Game {
private:
// Estructuras
struct DemoData {
int counter; // Contador para el modo demo
int room_time; // Tiempo que se muestra cada habitación
int room_index; // Índice para el vector de habitaciones
std::vector<std::string> rooms; // Listado con los mapas de la demo
// Constructor por defecto
DemoData()
: counter(0),
room_time(0),
room_index(0),
rooms({}) {}
// Constructor parametrizado
DemoData(int counter, int room_time, int room_index, const std::vector<std::string>& rooms)
: counter(counter),
room_time(room_time),
room_index(room_index),
rooms(rooms) {}
};
// Objetos y punteros
std::shared_ptr<ScoreboardData> board_; // Estructura con los datos del marcador
std::shared_ptr<Scoreboard> scoreboard_; // Objeto encargado de gestionar el marcador
std::shared_ptr<RoomTracker> room_tracker_; // Lleva el control de las habitaciones visitadas
std::shared_ptr<Room> room_; // Objeto encargado de gestionar cada habitación del juego
std::shared_ptr<Player> player_; // Objeto con el jugador
std::shared_ptr<Stats> stats_; // Objeto encargado de gestionar las estadísticas
std::shared_ptr<Surface> room_name_surface_; // Textura para escribir el nombre de la habitación
// Variables
GameMode mode_; // Modo del juego
DemoData demo_; // Variables para el modo demo
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::string current_room_; // Fichero de la habitación actual
PlayerSpawn spawn_point_; // Lugar de la habitación donde aparece el jugador
bool paused_ = false; // Indica si el juego se encuentra en pausa
bool black_screen_ = false; // Indica si la pantalla está en negro. Se utiliza para la muerte del jugador
int black_screen_counter_ = 0; // Contador para temporizar la pantalla en negro
int total_items_; // Cantidad total de items que hay en el mapeado del juego
SDL_Rect room_name_rect_; // Rectangulo donde pintar la textura con el nombre de la habitación
// Actualiza el juego, las variables, comprueba la entrada, etc.
void update();
// Pinta los objetos en pantalla
void render();
// Comprueba los eventos de la cola
void checkEvents();
#ifdef DEBUG
// Pone la información de debug en pantalla
void updateDebugInfo();
// Pone la información de debug en pantalla
void renderDebugInfo();
// Comprueba los eventos
void checkDebugEvents(const SDL_Event& event);
#endif
// Escribe el nombre de la pantalla
void renderRoomName();
// Cambia de habitación
bool changeRoom(const std::string& file);
// Comprueba el teclado
void checkInput();
// Comprueba si el jugador esta en el borde de la pantalla y actua
void checkPlayerIsOnBorder();
// Comprueba las colisiones del jugador con los enemigos
bool checkPlayerAndEnemies();
// Comprueba las colisiones del jugador con los objetos
void checkPlayerAndItems();
// Comprueba si el jugador esta vivo
void checkIfPlayerIsAlive();
// Comprueba si ha terminado la partida
void checkGameOver();
// Mata al jugador
void killPlayer();
// Establece la pantalla en negro
void setBlackScreen();
// Actualiza las variables relativas a la pantalla en negro
void updateBlackScreen();
// Dibuja la pantalla negra
void renderBlackScreen();
// Pone el color del marcador en función del color del borde de la habitación
void setScoreBoardColor();
// Comprueba si ha finalizado el juego
bool checkEndGame();
// Obtiene la cantidad total de items que hay en el mapeado del juego
int getTotalItems();
// Pone el juego en pausa
void togglePause();
// Da vidas al jugador cuando está en la Jail
void checkRestoringJail();
// Inicializa el diccionario de las estadísticas
void initStats();
// Pone el nombre de la habitación en la textura
void fillRoomNameTexture();
// Comprueba algunos logros
void checkSomeCheevos();
// Comprueba los logros de completar el juego
void checkEndGameCheevos();
// Inicializa al jugador
void initPlayer(const PlayerSpawn& spawn_point, std::shared_ptr<Room> room);
// Crea la textura para poner el nombre de la habitación
void createRoomNameTexture();
// Hace sonar la música
void keepMusicPlaying();
// DEMO MODE: Inicializa las variables para el modo demo
void DEMO_init();
// DEMO MODE: Comprueba si se ha de cambiar de habitación
void DEMO_checkRoomChange();
public:
// Constructor
explicit Game(GameMode mode);
// Destructor
~Game();
// Bucle para el juego
void run();
};

View File

@@ -1,8 +1,11 @@
#include "game_over.h" #include "game_over.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para min, max #include <algorithm> // Para min, max
#include <string> // Para basic_string, operator+, to_string #include <string> // Para basic_string, operator+, to_string
#include "defines.h" // Para GAMECANVAS_CENTER_X, GAME_SPEED #include "defines.h" // Para GAMECANVAS_CENTER_X, GAME_SPEED
#include "global_events.h" // Para check #include "global_events.h" // Para check
#include "global_inputs.h" // Para check #include "global_inputs.h" // Para check
@@ -20,8 +23,7 @@ GameOver::GameOver()
tv_sprite_(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface("tv.gif"), Resource::get()->getAnimations("tv.ani"))), tv_sprite_(std::make_shared<SAnimatedSprite>(Resource::get()->getSurface("tv.gif"), Resource::get()->getAnimations("tv.ani"))),
pre_counter_(0), pre_counter_(0),
counter_(0), counter_(0),
ticks_(0) ticks_(0) {
{
options.section.section = Section::GAME_OVER; options.section.section = Section::GAME_OVER;
options.section.subsection = Subsection::NONE; options.section.subsection = Subsection::NONE;
@@ -34,19 +36,16 @@ GameOver::GameOver()
// Inicializa el vector de colores // Inicializa el vector de colores
const std::vector<std::string> COLORS = {"white", "yellow", "cyan", "green", "magenta", "red", "blue", "black"}; const std::vector<std::string> COLORS = {"white", "yellow", "cyan", "green", "magenta", "red", "blue", "black"};
for (const auto &color : COLORS) for (const auto& color : COLORS) {
{
colors_.push_back(stringToColor(color)); colors_.push_back(stringToColor(color));
} }
color_ = colors_.back(); color_ = colors_.back();
} }
// Actualiza el objeto // Actualiza el objeto
void GameOver::update() void GameOver::update() {
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
{
// Actualiza el contador de ticks // Actualiza el contador de ticks
ticks_ = SDL_GetTicks(); ticks_ = SDL_GetTicks();
@@ -69,8 +68,7 @@ void GameOver::update()
} }
// Dibuja el final en pantalla // Dibuja el final en pantalla
void GameOver::render() void GameOver::render() {
{
constexpr int Y = 32; constexpr int Y = 32;
Screen::get()->start(); Screen::get()->start();
@@ -101,26 +99,21 @@ void GameOver::render()
} }
// Comprueba el manejador de eventos // Comprueba el manejador de eventos
void GameOver::checkEvents() void GameOver::checkEvents() {
{
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event)) {
{
globalEvents::check(event); globalEvents::check(event);
} }
} }
// Comprueba las entradas // Comprueba las entradas
void GameOver::checkInput() void GameOver::checkInput() {
{
globalInputs::check(); globalInputs::check();
} }
// Bucle principal // Bucle principal
void GameOver::run() void GameOver::run() {
{ while (options.section.section == Section::GAME_OVER) {
while (options.section.section == Section::GAME_OVER)
{
update(); update();
checkEvents(); checkEvents();
render(); render();
@@ -128,18 +121,14 @@ void GameOver::run()
} }
// Actualiza el color usado para renderizar los textos e imagenes // Actualiza el color usado para renderizar los textos e imagenes
void GameOver::updateColor() void GameOver::updateColor() {
{
const int half = COUNTER_SECTION_END_ / 2; const int half = COUNTER_SECTION_END_ / 2;
if (counter_ < half) if (counter_ < half) {
{
const float STEP = std::min(counter_, COUNTER_FADE_LENGHT_) / (float)COUNTER_FADE_LENGHT_; const float STEP = std::min(counter_, COUNTER_FADE_LENGHT_) / (float)COUNTER_FADE_LENGHT_;
const int INDEX = (colors_.size() - 1) - int((colors_.size() - 1) * STEP); const int INDEX = (colors_.size() - 1) - int((colors_.size() - 1) * STEP);
color_ = colors_[INDEX]; color_ = colors_[INDEX];
} } else {
else
{
const float STEP = std::min(std::max(counter_, COUNTER_INIT_FADE_) - COUNTER_INIT_FADE_, COUNTER_FADE_LENGHT_) / (float)COUNTER_FADE_LENGHT_; const float STEP = std::min(std::max(counter_, COUNTER_INIT_FADE_) - COUNTER_INIT_FADE_, COUNTER_FADE_LENGHT_) / (float)COUNTER_FADE_LENGHT_;
const int INDEX = (colors_.size() - 1) * STEP; const int INDEX = (colors_.size() - 1) * STEP;
color_ = colors_[INDEX]; color_ = colors_[INDEX];
@@ -147,34 +136,27 @@ void GameOver::updateColor()
} }
// Dibuja los sprites // Dibuja los sprites
void GameOver::renderSprites() void GameOver::renderSprites() {
{
player_sprite_->render(1, color_); player_sprite_->render(1, color_);
tv_sprite_->render(1, color_); tv_sprite_->render(1, color_);
} }
// Actualiza los contadores // Actualiza los contadores
void GameOver::updateCounters() void GameOver::updateCounters() {
{
// Actualiza el contador // Actualiza el contador
if (pre_counter_ < 50) if (pre_counter_ < 50) {
{
pre_counter_++; pre_counter_++;
} } else {
else
{
counter_++; counter_++;
} }
// Hace sonar la música // Hace sonar la música
if (counter_ == 1) if (counter_ == 1) {
{
JA_PlayMusic(Resource::get()->getMusic("game_over.ogg"), 0); JA_PlayMusic(Resource::get()->getMusic("game_over.ogg"), 0);
} }
// Comprueba si ha terminado la sección // Comprueba si ha terminado la sección
else if (counter_ == COUNTER_SECTION_END_) else if (counter_ == COUNTER_SECTION_END_) {
{
options.section.section = Section::LOGO; options.section.section = Section::LOGO;
options.section.subsection = Subsection::LOGO_TO_TITLE; options.section.subsection = Subsection::LOGO_TO_TITLE;
} }

View File

@@ -0,0 +1,57 @@
#pragma once
#include <SDL3/SDL_stdinc.h> // Para Uint8, Uint32
#include <memory> // Para shared_ptr
#include <vector> // Para vector
class SAnimatedSprite; // lines 7-7
class GameOver {
private:
// Constantes
static constexpr int COUNTER_SECTION_END_ = 400; // Contador: cuando acaba la sección
static constexpr int COUNTER_INIT_FADE_ = 310; // Contador: cuando emiepza el fade
static constexpr int COUNTER_FADE_LENGHT_ = 20; // Contador: duración del fade
// Objetos y punteros
std::shared_ptr<SAnimatedSprite> player_sprite_; // Sprite con el jugador
std::shared_ptr<SAnimatedSprite> tv_sprite_; // Sprite con el televisor
// Variables
int pre_counter_ = 0; // Contador previo
int counter_ = 0; // Contador
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<Uint8> colors_; // Vector con los colores para el fade
Uint8 color_; // Color usado para el texto y los sprites
// Actualiza el objeto
void update();
// Dibuja el final en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Actualiza el color usado para renderizar los textos e imagenes
void updateColor();
// Dibuja los sprites
void renderSprites();
// Actualiza los contadores
void updateCounters();
public:
// Constructor
GameOver();
// Destructor
~GameOver() = default;
// Bucle principal
void run();
};

View File

@@ -0,0 +1,200 @@
#include "loading_screen.h"
#include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <stdlib.h> // Para rand
#include "defines.h" // Para GAME_SPEED
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "jail_audio.h" // Para JA_PlayMusic, JA_SetVolume, JA_StopMusic
#include "options.h" // Para Options, options, SectionState, Options...
#include "resource.h" // Para Resource
#include "s_sprite.h" // Para SSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "utils.h" // Para stringToColor, PaletteColor
// Constructor
LoadingScreen::LoadingScreen()
: mono_loading_screen_surface_(Resource::get()->getSurface("loading_screen_bn.gif")),
color_loading_screen_surface_(Resource::get()->getSurface("loading_screen_color.gif")),
mono_loading_screen_sprite_(std::make_shared<SSprite>(mono_loading_screen_surface_, 0, 0, mono_loading_screen_surface_->getWidth(), mono_loading_screen_surface_->getHeight())),
color_loading_screen_sprite_(std::make_shared<SSprite>(color_loading_screen_surface_, 0, 0, color_loading_screen_surface_->getWidth(), color_loading_screen_surface_->getHeight())),
screen_surface_(std::make_shared<Surface>(options.game.width, options.game.height)) {
// Configura la superficie donde se van a pintar los sprites
screen_surface_->clear(static_cast<Uint8>(PaletteColor::WHITE));
// Inicializa variables
options.section.section = Section::LOADING_SCREEN;
options.section.subsection = Subsection::NONE;
// Establece el orden de las lineas para imitar el direccionamiento de memoria del spectrum
for (int i = 0; i < 192; ++i) {
if (i < 64) { // Primer bloque de 2K
line_index_[i] = ((i % 8) * 8) + (i / 8);
} else if (i < 128) { // Segundo bloque de 2K
line_index_[i] = 64 + ((i % 8) * 8) + ((i - 64) / 8);
} else { // Tercer bloque de 2K
line_index_[i] = 128 + ((i % 8) * 8) + ((i - 128) / 8);
}
}
// Cambia el color del borde
Screen::get()->setBorderColor(stringToColor("black"));
}
// Destructor
LoadingScreen::~LoadingScreen() {
JA_StopMusic();
}
// Comprueba el manejador de eventos
void LoadingScreen::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
globalEvents::check(event);
}
}
// Comprueba las entradas
void LoadingScreen::checkInput() {
globalInputs::check();
}
// Gestiona el contador de carga
void LoadingScreen::updateLoad() {
// Primera parte de la carga, la parte en blanco y negro
if (loading_first_part_) {
// Cada 5 pasos el load_counter_ se incrementa en uno
constexpr int NUM_STEPS = 5;
constexpr int STEPS = 51;
load_counter_ = counter_ / NUM_STEPS;
if (load_counter_ < 192) {
load_rect_.x = STEPS * (counter_ % NUM_STEPS);
load_rect_.y = line_index_[load_counter_];
mono_loading_screen_sprite_->setClip(load_rect_);
mono_loading_screen_sprite_->setPosition(load_rect_);
}
// Una vez actualizadas las 192 lineas, pasa a la segunda fase de la carga
else if (load_counter_ == 192) {
loading_first_part_ = false;
load_counter_ = 0;
load_rect_ = {0, 0, 16, 8};
color_loading_screen_sprite_->setClip(load_rect_);
color_loading_screen_sprite_->setPosition(load_rect_);
JA_PlayMusic(Resource::get()->getMusic("loading_sound3.ogg"));
}
}
// Segunda parte de la carga, la parte de los bloques en color
else {
load_counter_ += 2;
load_rect_.x = (load_counter_ * 8) % 256;
load_rect_.y = (load_counter_ / 32) * 8;
color_loading_screen_sprite_->setClip(load_rect_);
color_loading_screen_sprite_->setPosition(load_rect_);
// Comprueba si ha terminado la intro
if (load_counter_ >= 768) {
options.section.section = Section::TITLE;
options.section.subsection = Subsection::TITLE_WITH_LOADING_SCREEN;
JA_StopMusic();
}
}
}
// Gestiona el contador interno
void LoadingScreen::updateCounter() {
(pre_counter_ >= 50) ? counter_++ : pre_counter_++;
if (counter_ == 1) {
JA_PlayMusic(Resource::get()->getMusic("loading_sound2.ogg"));
}
}
// Dibuja la pantalla de carga
void LoadingScreen::renderLoad() {
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(screen_surface_);
loading_first_part_ ? mono_loading_screen_sprite_->render(1, stringToColor("black")) : color_loading_screen_sprite_->render();
Screen::get()->setRendererSurface(previuos_renderer);
}
// Dibuja el efecto de carga en el borde
void LoadingScreen::renderBorder() {
// Obtiene la Surface del borde
auto border = Screen::get()->getBorderSurface();
// Pinta el borde de color azul
border->clear(static_cast<Uint8>(PaletteColor::BLUE));
// Añade lineas amarillas
const Uint8 COLOR = static_cast<Uint8>(PaletteColor::YELLOW);
const int WIDTH = options.game.width + (options.video.border.width * 2);
const int HEIGHT = options.game.height + (options.video.border.height * 2);
bool draw_enabled = rand() % 2 == 0 ? true : false;
int row = 0;
while (row < HEIGHT) {
const int ROW_HEIGHT = (rand() % 4) + 3;
if (draw_enabled) {
for (int i = row; i < row + ROW_HEIGHT; ++i) {
border->drawLine(0, i, WIDTH, i, COLOR);
}
}
row += ROW_HEIGHT;
draw_enabled = !draw_enabled;
}
}
// Actualiza las variables
void LoadingScreen::update() {
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
ticks_ = SDL_GetTicks();
checkInput();
updateCounter();
updateLoad();
renderLoad();
Screen::get()->update();
}
}
// Dibuja en pantalla
void LoadingScreen::render() {
if (options.video.border.enabled) {
// Dibuja el efecto de carga en el borde
renderBorder();
}
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
Screen::get()->clearSurface(stringToColor("white"));
// Copia la surface a la surface de Screen
screen_surface_->render(0, 0);
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void LoadingScreen::run() {
// Inicia el sonido de carga
JA_SetVolume(64);
JA_PlayMusic(Resource::get()->getMusic("loading_sound1.ogg"));
// Limpia la pantalla
Screen::get()->start();
Screen::get()->clearRenderer();
Screen::get()->render();
while (options.section.section == Section::LOADING_SCREEN) {
update();
checkEvents();
render();
}
JA_SetVolume(128);
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_stdinc.h> // Para Uint32
#include <memory> // Para shared_ptr
class SSprite; // lines 7-7
class Surface; // lines 8-8
class LoadingScreen {
private:
// Objetos y punteros
std::shared_ptr<Surface> mono_loading_screen_surface_; // Surface con la pantalla de carga en blanco y negro
std::shared_ptr<Surface> color_loading_screen_surface_; // Surface con la pantalla de carga en color
std::shared_ptr<SSprite> mono_loading_screen_sprite_; // SSprite para manejar la textura loadingScreenTexture1
std::shared_ptr<SSprite> color_loading_screen_sprite_; // SSprite para manejar la textura loadingScreenTexture2
std::shared_ptr<Surface> screen_surface_; // Surface para dibujar la pantalla de carga
// Variables
int pre_counter_ = 0; // Contador previo para realizar una pausa inicial
int counter_ = 0; // Contador
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
int load_counter_ = 0; // Contador para controlar las cargas
bool loading_first_part_ = true; // Para saber en que parte de la carga se encuentra
int line_index_[192]; // El orden en el que se procesan las 192 lineas de la pantalla de carga
SDL_Rect load_rect_ = {0, 0, 52, 1}; // Rectangulo para dibujar la pantalla de carga
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Gestiona el contador interno
void updateCounter();
// Gestiona el contador de carga
void updateLoad();
// Dibuja la pantalla de carga
void renderLoad();
// Dibuja el efecto de carga en el borde
void renderBorder();
public:
// Constructor
LoadingScreen();
// Destructor
~LoadingScreen();
// Bucle principal
void run();
};

226
source/sections/logo.cpp Normal file
View File

@@ -0,0 +1,226 @@
#include "logo.h"
#include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include "defines.h" // Para GAME_SPEED
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "options.h" // Para Options, SectionState, options, Section
#include "resource.h" // Para Resource
#include "s_sprite.h" // Para SSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "utils.h" // Para PaletteColor
// Constructor
Logo::Logo()
: jailgames_surface_(Resource::get()->getSurface("jailgames.gif")),
since_1998_surface_(Resource::get()->getSurface("since_1998.gif")),
since_1998_sprite_(std::make_shared<SSprite>(since_1998_surface_, (256 - since_1998_surface_->getWidth()) / 2, 83 + jailgames_surface_->getHeight() + 5, since_1998_surface_->getWidth(), since_1998_surface_->getHeight())) {
since_1998_sprite_->setClip(0, 0, since_1998_surface_->getWidth(), since_1998_surface_->getHeight());
since_1998_color_ = static_cast<Uint8>(PaletteColor::BLACK);
jailgames_color_ = static_cast<Uint8>(PaletteColor::BRIGHT_WHITE);
// Crea los sprites de cada linea
for (int i = 0; i < jailgames_surface_->getHeight(); ++i) {
jailgames_sprite_.push_back(std::make_shared<SSprite>(jailgames_surface_, 0, i, jailgames_surface_->getWidth(), 1));
jailgames_sprite_.back()->setClip(0, i, jailgames_surface_->getWidth(), 1);
jailgames_sprite_.at(i)->setX((i % 2 == 0) ? (256 + (i * 3)) : (-181 - (i * 3)));
jailgames_sprite_.at(i)->setY(83 + i);
}
// Inicializa variables
options.section.section = Section::LOGO;
// Inicializa el vector de colores
const std::vector<Uint8> COLORS = {
static_cast<Uint8>(PaletteColor::BLACK),
static_cast<Uint8>(PaletteColor::BLUE),
static_cast<Uint8>(PaletteColor::RED),
static_cast<Uint8>(PaletteColor::MAGENTA),
static_cast<Uint8>(PaletteColor::GREEN),
static_cast<Uint8>(PaletteColor::CYAN),
static_cast<Uint8>(PaletteColor::YELLOW),
static_cast<Uint8>(PaletteColor::BRIGHT_WHITE)};
for (const auto& color : COLORS) {
color_.push_back(color);
}
// Cambia el color del borde
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
}
// Comprueba el manejador de eventos
void Logo::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
globalEvents::check(event);
}
}
// Comprueba las entradas
void Logo::checkInput() {
globalInputs::check();
}
// Gestiona el logo de JAILGAME
void Logo::updateJAILGAMES() {
if (counter_ > 30) {
for (int i = 1; i < (int)jailgames_sprite_.size(); ++i) {
constexpr int SPEED = 8;
constexpr int DEST = 37;
if (jailgames_sprite_.at(i)->getX() != 37) {
if (i % 2 == 0) {
jailgames_sprite_.at(i)->incX(-SPEED);
if (jailgames_sprite_.at(i)->getX() < DEST) {
jailgames_sprite_.at(i)->setX(DEST);
}
} else {
jailgames_sprite_.at(i)->incX(SPEED);
if (jailgames_sprite_.at(i)->getX() > DEST) {
jailgames_sprite_.at(i)->setX(DEST);
}
}
}
}
}
}
// Gestiona el color de las texturas
void Logo::updateTextureColors() {
constexpr int INI = 70;
constexpr int INC = 4;
if (counter_ == INI + INC * 0) {
since_1998_color_ = color_.at(0);
}
else if (counter_ == INI + INC * 1) {
since_1998_color_ = color_.at(1);
}
else if (counter_ == INI + INC * 2) {
since_1998_color_ = color_.at(2);
}
else if (counter_ == INI + INC * 3) {
since_1998_color_ = color_.at(3);
}
else if (counter_ == INI + INC * 4) {
since_1998_color_ = color_.at(4);
}
else if (counter_ == INI + INC * 5) {
since_1998_color_ = color_.at(5);
}
else if (counter_ == INI + INC * 6) {
since_1998_color_ = color_.at(6);
}
else if (counter_ == INI + INC * 7) {
since_1998_color_ = color_.at(7);
}
else if (counter_ == INIT_FADE_ + INC * 0) {
jailgames_color_ = color_.at(6);
since_1998_color_ = color_.at(6);
}
else if (counter_ == INIT_FADE_ + INC * 1) {
jailgames_color_ = color_.at(5);
since_1998_color_ = color_.at(5);
}
else if (counter_ == INIT_FADE_ + INC * 2) {
jailgames_color_ = color_.at(4);
since_1998_color_ = color_.at(4);
}
else if (counter_ == INIT_FADE_ + INC * 3) {
jailgames_color_ = color_.at(3);
since_1998_color_ = color_.at(3);
}
else if (counter_ == INIT_FADE_ + INC * 4) {
jailgames_color_ = color_.at(2);
since_1998_color_ = color_.at(2);
}
else if (counter_ == INIT_FADE_ + INC * 5) {
jailgames_color_ = color_.at(1);
since_1998_color_ = color_.at(1);
}
else if (counter_ == INIT_FADE_ + INC * 6) {
jailgames_color_ = color_.at(0);
since_1998_color_ = color_.at(0);
}
}
// Actualiza las variables
void Logo::update() {
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba las entradas
checkInput();
// Incrementa el contador
counter_++;
// Gestiona el logo de JAILGAME
updateJAILGAMES();
// Gestiona el color de las texturas
updateTextureColors();
// Actualiza el objeto Screen
Screen::get()->update();
// Comprueba si ha terminado el logo
if (counter_ == END_LOGO_ + POST_LOGO_) {
endSection();
}
}
}
// Dibuja en pantalla
void Logo::render() {
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));
// Dibuja los objetos
for (const auto& s : jailgames_sprite_) {
s->render(1, jailgames_color_);
}
since_1998_sprite_->render(1, since_1998_color_);
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void Logo::run() {
while (options.section.section == Section::LOGO) {
update();
checkEvents();
render();
}
}
// Termina la sección
void Logo::endSection() {
if (options.section.subsection == Subsection::LOGO_TO_TITLE) {
options.section.section = Section::TITLE;
}
else if (options.section.subsection == Subsection::LOGO_TO_INTRO) {
options.section.section = Section::LOADING_SCREEN;
}
}

60
source/sections/logo.h Normal file
View File

@@ -0,0 +1,60 @@
#pragma once
#include <SDL3/SDL_stdinc.h> // Para Uint8, Uint32
#include <memory> // Para shared_ptr
#include <vector> // Para vector
class SSprite; // lines 7-7
class Surface; // lines 8-8
class Logo {
private:
// Constantes
static constexpr int INIT_FADE_ = 300; // Tiempo del contador cuando inicia el fade a negro
static constexpr int END_LOGO_ = 400; // Tiempo del contador para terminar el logo
static constexpr int POST_LOGO_ = 20; // Tiempo que dura el logo con el fade al maximo
// Objetos y punteros
std::shared_ptr<Surface> jailgames_surface_; // Textura con los graficos "JAILGAMES"
std::shared_ptr<Surface> since_1998_surface_; // Textura con los graficos "Since 1998"
std::vector<std::shared_ptr<SSprite>> jailgames_sprite_; // Vector con los sprites de cada linea que forman el bitmap JAILGAMES
std::shared_ptr<SSprite> since_1998_sprite_; // SSprite para manejar la textura2
Uint8 jailgames_color_ = 0; // Color para el sprite de "JAILGAMES"
Uint8 since_1998_color_ = 0; // Color para el sprite de "Since 1998"
// Variables
std::vector<Uint8> color_; // Vector con los colores para el fade
int counter_ = 0; // Contador
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Gestiona el logo de JAILGAME
void updateJAILGAMES();
// Gestiona el color de las texturas
void updateTextureColors();
// Termina la sección
void endSection();
public:
// Constructor
Logo();
// Destructor
~Logo() = default;
// Bucle principal
void run();
};

334
source/sections/title.cpp Normal file
View File

@@ -0,0 +1,334 @@
#include "title.h"
#include <SDL3/SDL_events.h> // Para SDL_PollEvent, SDL_Event, SDL_KEYDOWN
#include <SDL3/SDL_scancode.h> // Para SDL_SCANCODE_1, SDL_SCANCODE_2
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para clamp
#include "cheevos.h" // Para Cheevos, Achievement
#include "defines.h" // Para PLAY_AREA_CENTER_X, GAMECANVAS_WIDTH
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, InputAction, INPUT_DO_NOT_ALLOW_REPEAT, REP...
#include "options.h" // Para Options, options, SectionState, Section
#include "resource.h" // Para Resource
#include "s_sprite.h" // Para SSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "utils.h" // Para stringToColor, PaletteColor, playMusic
// Constructor
Title::Title()
: title_logo_surface_(Resource::get()->getSurface("title_logo.gif")),
title_logo_sprite_(std::make_shared<SSprite>(title_logo_surface_, 29, 9, title_logo_surface_->getWidth(), title_logo_surface_->getHeight())),
loading_screen_surface_(Resource::get()->getSurface("loading_screen_color.gif")),
loading_screen_sprite_(std::make_shared<SSprite>(loading_screen_surface_, 0, 0, loading_screen_surface_->getWidth(), loading_screen_surface_->getHeight())),
bg_surface_(std::make_shared<Surface>(options.game.width, options.game.height)) {
// Inicializa variables
state_ = options.section.subsection == Subsection::TITLE_WITH_LOADING_SCREEN ? TitleState::SHOW_LOADING_SCREEN : TitleState::SHOW_MENU;
options.section.section = Section::TITLE;
options.section.subsection = Subsection::NONE;
initMarquee();
// Crea y rellena la textura para mostrar los logros
createCheevosTexture();
// Cambia el color del borde
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
// Rellena la textura de fondo con todos los gráficos
fillSurface();
// Inicia la musica
playMusic("title.ogg");
}
// Inicializa la marquesina
void Title::initMarquee() {
letters_.clear();
long_text_ = "HEY JAILERS!! IT'S 2022 AND WE'RE STILL ROCKING LIKE IT'S 1998!!! HAVE YOU HEARD IT? JAILGAMES ARE BACK!! YEEESSS BACK!! MORE THAN 10 TITLES ON JAILDOC'S KITCHEN!! THATS A LOOOOOOT OF JAILGAMES, BUT WHICH ONE WILL STRIKE FIRST? THERE IS ALSO A NEW DEVICE TO COME THAT WILL BLOW YOUR MIND WITH JAILGAMES ON THE GO: P.A.C.O. BUT WAIT! WHAT'S THAT BEAUTY I'M SEEING RIGHT OVER THERE?? OOOH THAT TINY MINIASCII IS PURE LOVE!! I WANT TO LICK EVERY BYTE OF IT!! OH SHIT! AND DON'T FORGET TO BRING BACK THOSE OLD AND FAT MS-DOS JAILGAMES TO GITHUB TO KEEP THEM ALIVE!! WHAT WILL BE THE NEXT JAILDOC RELEASE? WHAT WILL BE THE NEXT PROJECT TO COME ALIVE?? OH BABY WE DON'T KNOW BUT HERE YOU CAN FIND THE ANSWER, YOU JUST HAVE TO COMPLETE JAILDOCTOR'S DILEMMA ... COULD YOU?";
for (int i = 0; i < (int)long_text_.length(); ++i) {
TitleLetter l;
l.letter = long_text_.substr(i, 1);
l.x = 256;
l.enabled = false;
letters_.push_back(l);
}
letters_[0].enabled = true;
}
// Comprueba el manejador de eventos
void Title::checkEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
globalEvents::check(event);
// Solo se comprueban estas teclas si no está activo el menu de logros
if (event.type == SDL_KEYDOWN) {
if (!show_cheevos_) {
switch (event.key.keysym.scancode) {
case SDL_SCANCODE_1:
options.section.section = Section::GAME;
options.section.subsection = Subsection::NONE;
break;
case SDL_SCANCODE_2:
show_cheevos_ = true;
break;
default:
break;
}
}
}
}
}
// Comprueba las entradas
void Title::checkInput() {
if (show_cheevos_) {
if (Input::get()->checkInput(InputAction::DOWN, INPUT_ALLOW_REPEAT)) {
moveCheevosList(1);
} else if (Input::get()->checkInput(InputAction::UP, INPUT_ALLOW_REPEAT)) {
moveCheevosList(0);
} else if (Input::get()->checkInput(InputAction::ACCEPT, INPUT_DO_NOT_ALLOW_REPEAT)) {
hideCheevosList();
counter_ = 0;
}
}
if (Input::get()->checkInput(InputAction::ACCEPT, INPUT_DO_NOT_ALLOW_REPEAT)) {
if (state_ == TitleState::SHOW_LOADING_SCREEN) {
state_ = TitleState::FADE_LOADING_SCREEN;
}
}
globalInputs::check();
}
// Actualiza la marquesina
void Title::updateMarquee() {
const auto TEXT = Resource::get()->getText("gauntlet");
for (int i = 0; i < (int)letters_.size(); ++i) {
if (letters_[i].enabled) {
letters_[i].x -= marquee_speed_;
if (letters_[i].x < -10) {
letters_[i].enabled = false;
}
} else {
if (i > 0 && letters_[i - 1].x < 256 && letters_[i - 1].enabled) {
letters_[i].enabled = true;
letters_[i].x = letters_[i - 1].x + TEXT->lenght(letters_[i - 1].letter) + 1;
}
}
}
// Comprueba si ha terminado la marquesina y la reinicia
if (letters_[letters_.size() - 1].x < -10) {
// Inicializa la marquesina
initMarquee();
}
}
// Dibuja la marquesina
void Title::renderMarquee() {
const auto TEXT = Resource::get()->getText("gauntlet");
for (const auto& l : letters_) {
if (l.enabled) {
TEXT->writeColored(l.x, 184, l.letter, static_cast<Uint8>(PaletteColor::BRIGHT_RED));
}
}
}
// Actualiza las variables
void Title::update() {
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba las entradas
checkInput();
Screen::get()->update();
// Incrementa el contador
counter_++;
switch (state_) {
case TitleState::SHOW_LOADING_SCREEN:
if (counter_ == 500) {
counter_ = 0;
state_ = TitleState::FADE_LOADING_SCREEN;
}
break;
case TitleState::FADE_LOADING_SCREEN:
if (counter_ % 4 == 0) {
if (loading_screen_surface_->fadeSubPalette()) {
counter_ = 0;
state_ = TitleState::SHOW_MENU;
}
}
break;
case TitleState::SHOW_MENU:
// Actualiza la marquesina
updateMarquee();
// Si el contador alcanza cierto valor, termina la seccion
if (counter_ == 2200) {
if (!show_cheevos_) {
options.section.section = Section::CREDITS;
options.section.subsection = Subsection::NONE;
}
}
break;
default:
break;
}
}
}
// Dibuja en pantalla
void Title::render() {
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));
switch (state_) {
case TitleState::SHOW_MENU:
// Dibuja la textura de fondo
bg_surface_->render(0, 0);
// Dibuja la marquesina
renderMarquee();
// Dibuja la información de logros
if (show_cheevos_) {
cheevos_sprite_->render();
}
break;
case TitleState::SHOW_LOADING_SCREEN:
case TitleState::FADE_LOADING_SCREEN:
loading_screen_sprite_->render();
title_logo_sprite_->render();
break;
default:
break;
}
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void Title::run() {
while (options.section.section == Section::TITLE) {
update();
checkEvents();
render();
}
}
// Desplaza la lista de logros
void Title::moveCheevosList(int direction) {
// Modifica la posición de la ventana de vista
constexpr int SPEED = 2;
cheevos_surface_view_.y = direction == 0 ? cheevos_surface_view_.y - SPEED : cheevos_surface_view_.y + SPEED;
// Ajusta los limites
const int BOTTOM = cheevos_surface_->getHeight() - cheevos_surface_view_.h;
cheevos_surface_view_.y = std::clamp(cheevos_surface_view_.y, 0, BOTTOM);
cheevos_sprite_->setClip(cheevos_surface_view_);
}
// Rellena la textura de fondo con todos los gráficos
void Title::fillSurface() {
// Coloca el puntero del renderizador sobre la textura
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(bg_surface_);
// Rellena la textura de color
bg_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
// Pinta el gráfico del titulo a partir del sprite
title_logo_sprite_->render();
// Escribe el texto en la textura
auto text = Resource::get()->getText("smb2");
const Uint8 COLOR = stringToColor("green");
const int TEXT_SIZE = text->getCharacterSize();
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 11 * TEXT_SIZE, "1.PLAY", 1, COLOR);
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 13 * TEXT_SIZE, "2.ACHIEVEMENTS", 1, COLOR);
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 15 * TEXT_SIZE, "3.REDEFINE KEYS", 1, COLOR);
// Devuelve el puntero del renderizador a su sitio
Screen::get()->setRendererSurface(previuos_renderer);
}
// Crea y rellena la textura para mostrar los logros
void Title::createCheevosTexture() {
// Crea la textura con el listado de logros
const auto CHEEVOS_LIST = Cheevos::get()->list();
const auto TEXT = Resource::get()->getText("subatomic");
constexpr int CHEEVOS_TEXTURE_WIDTH = 200;
constexpr int CHEEVOS_TEXTURE_VIEW_HEIGHT = 110 - 8;
constexpr int CHEEVOS_TEXTURE_POS_Y = 73;
constexpr int CHEEVOS_PADDING = 10;
const int CHEEVO_HEIGHT = CHEEVOS_PADDING + (TEXT->getCharacterSize() * 2) + 1;
const int CHEEVOS_TEXTURE_HEIGHT = (CHEEVO_HEIGHT * CHEEVOS_LIST.size()) + 2 + TEXT->getCharacterSize() + 8;
cheevos_surface_ = std::make_shared<Surface>(CHEEVOS_TEXTURE_WIDTH, CHEEVOS_TEXTURE_HEIGHT);
// Prepara para dibujar sobre la textura
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(cheevos_surface_);
// Rellena la textura con color sólido
const Uint8 CHEEVOS_BG_COLOR = static_cast<Uint8>(PaletteColor::BLACK);
cheevos_surface_->clear(CHEEVOS_BG_COLOR);
// Escribe la lista de logros en la textura
const std::string CHEEVOS_OWNER = "ACHIEVEMENTS";
const std::string CHEEVOS_LIST_CAPTION = CHEEVOS_OWNER + " (" + std::to_string(Cheevos::get()->getTotalUnlockedAchievements()) + " / " + std::to_string(Cheevos::get()->size()) + ")";
int pos = 2;
TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, cheevos_surface_->getWidth() / 2, pos, CHEEVOS_LIST_CAPTION, 1, stringToColor("bright_green"));
pos += TEXT->getCharacterSize();
const Uint8 CHEEVO_LOCKED_COLOR = stringToColor("white");
const Uint8 CHEEVO_UNLOCKED_COLOR = stringToColor("bright_green");
constexpr int LINE_X1 = (CHEEVOS_TEXTURE_WIDTH / 7) * 3;
constexpr int LINE_X2 = LINE_X1 + ((CHEEVOS_TEXTURE_WIDTH / 7) * 1);
for (const auto& cheevo : CHEEVOS_LIST) {
const Uint8 CHEEVO_COLOR = cheevo.completed ? CHEEVO_UNLOCKED_COLOR : CHEEVO_LOCKED_COLOR;
pos += CHEEVOS_PADDING;
constexpr int HALF = CHEEVOS_PADDING / 2;
cheevos_surface_->drawLine(LINE_X1, pos - HALF - 1, LINE_X2, pos - HALF - 1, CHEEVO_COLOR);
TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.caption, 1, CHEEVO_COLOR);
pos += TEXT->getCharacterSize() + 1;
TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.description, 1, CHEEVO_COLOR);
pos += TEXT->getCharacterSize();
}
// Restablece el RenderSurface
Screen::get()->setRendererSurface(previuos_renderer);
// Crea el sprite para el listado de logros
cheevos_sprite_ = std::make_shared<SSprite>(cheevos_surface_, (GAMECANVAS_WIDTH - cheevos_surface_->getWidth()) / 2, CHEEVOS_TEXTURE_POS_Y, cheevos_surface_->getWidth(), cheevos_surface_->getHeight());
cheevos_surface_view_ = {0, 0, cheevos_surface_->getWidth(), CHEEVOS_TEXTURE_VIEW_HEIGHT};
cheevos_sprite_->setClip(cheevos_surface_view_);
}
// Oculta la lista de logros
void Title::hideCheevosList() {
show_cheevos_ = false;
cheevos_surface_view_.y = 0;
cheevos_sprite_->setClip(cheevos_surface_view_);
}

87
source/sections/title.h Normal file
View File

@@ -0,0 +1,87 @@
#pragma once
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_stdinc.h> // Para Uint32
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
class SSprite; // lines 9-9
class Surface; // lines 10-10
class Title {
private:
struct TitleLetter {
std::string letter; // Letra a escribir
int x; // Posición en el eje x
bool enabled; // Solo se escriben y mueven si estan habilitadas
};
enum class TitleState {
SHOW_LOADING_SCREEN,
FADE_LOADING_SCREEN,
SHOW_MENU
};
// Objetos y punteros
std::shared_ptr<Surface> title_logo_surface_; // Textura con los graficos
std::shared_ptr<SSprite> title_logo_sprite_; // SSprite para manejar la surface
std::shared_ptr<Surface> loading_screen_surface_; // Surface con los gráficos de la pantalla de carga
std::shared_ptr<SSprite> loading_screen_sprite_; // SSprite con los gráficos de la pantalla de carga
std::shared_ptr<Surface> bg_surface_; // Textura para dibujar el fondo de la pantalla
std::shared_ptr<Surface> cheevos_surface_; // Textura con la lista de logros
std::shared_ptr<SSprite> cheevos_sprite_; // SSprite para manejar la surface con la lista de logros
// Variables
int counter_ = 0; // Contador
std::string long_text_; // Texto que aparece en la parte inferior del titulo
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<TitleLetter> letters_; // Vector con las letras de la marquesina
int marquee_speed_ = 2; // Velocidad de desplazamiento de la marquesina
bool show_cheevos_ = false; // Indica si se muestra por pantalla el listado de logros
SDL_Rect cheevos_surface_view_; // Zona visible de la surface con el listado de logros
TitleState state_; // Estado en el que se encuentra el bucle principal
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Inicializa la marquesina
void initMarquee();
// Actualiza la marquesina
void updateMarquee();
// Dibuja la marquesina
void renderMarquee();
// Desplaza la lista de logros
void moveCheevosList(int direction);
// Rellena la surface de fondo con todos los gráficos
void fillSurface();
// Crea y rellena la surface para mostrar los logros
void createCheevosTexture();
// Oculta la lista de logros
void hideCheevosList();
public:
// Constructor
Title();
// Destructor
~Title() = default;
// Bucle principal
void run();
};

View File

@@ -0,0 +1,78 @@
#pragma once
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
#include "s_moving_sprite.h" // Para SMovingSprite
class Surface; // lines 9-9
struct AnimationData {
std::string name; // Nombre de la animacion
std::vector<SDL_Rect> frames; // Cada uno de los frames que componen la animación
int speed; // Velocidad de la animación
int loop; // Indica a que frame vuelve la animación al terminar. -1 para que no vuelva
bool completed; // Indica si ha finalizado la animación
int current_frame; // Frame actual
int counter; // Contador para las animaciones
AnimationData()
: name(std::string()),
speed(5),
loop(0),
completed(false),
current_frame(0),
counter(0) {}
};
using Animations = std::vector<std::string>;
// Carga las animaciones en un vector(Animations) desde un fichero
Animations loadAnimationsFromFile(const std::string& file_path);
class SAnimatedSprite : public SMovingSprite {
protected:
// Variables
std::vector<AnimationData> animations_; // Vector con las diferentes animaciones
int current_animation_ = 0; // Animacion activa
// Calcula el frame correspondiente a la animación actual
void animate();
// Carga la animación desde un vector de cadenas
void setAnimations(const Animations& animations);
public:
// Constructor
SAnimatedSprite(std::shared_ptr<Surface> surface, const std::string& file_path);
SAnimatedSprite(std::shared_ptr<Surface> surface, const Animations& animations);
explicit SAnimatedSprite(std::shared_ptr<Surface> surface)
: SMovingSprite(surface) {}
// Destructor
virtual ~SAnimatedSprite() override = default;
// Actualiza las variables del objeto
void update() override;
// Comprueba si ha terminado la animación
bool animationIsCompleted();
// Obtiene el indice de la animación a partir del nombre
int getIndex(const std::string& name);
// Establece la animacion actual
void setCurrentAnimation(const std::string& name = "default");
void setCurrentAnimation(int index = 0);
// Reinicia la animación
void resetAnimation();
// Establece el frame actual de la animación
void setCurrentAnimationFrame(int num);
// Obtiene el numero de frames de la animación actual
int getCurrentAnimationSize() { return static_cast<int>(animations_[current_animation_].frames.size()); }
};

View File

@@ -0,0 +1,83 @@
#pragma once
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_render.h> // Para SDL_RendererFlip, SDL_FLIP_HORIZONTAL
#include <SDL3/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr
#include "s_sprite.h" // Para SSprite
class Surface; // lines 8-8
// Clase SMovingSprite. Añade movimiento y flip al sprite
class SMovingSprite : public SSprite {
public:
protected:
float x_; // Posición en el eje X
float y_; // Posición en el eje Y
float vx_ = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
float vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles 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
SDL_RendererFlip flip_; // Indica como se voltea el sprite
// Mueve el sprite
void move();
public:
// Constructor
SMovingSprite(std::shared_ptr<Surface> surface, SDL_Rect pos, SDL_RendererFlip flip);
SMovingSprite(std::shared_ptr<Surface> surface, SDL_Rect pos);
explicit SMovingSprite(std::shared_ptr<Surface> surface);
// Destructor
virtual ~SMovingSprite() override = default;
// Actualiza las variables internas del objeto
virtual void update();
// Reinicia todas las variables a cero
void clear() override;
// Muestra el sprite por pantalla
void render() override;
void render(Uint8 source_color, Uint8 target_color) override;
// Obtiene la variable
float getPosX() const { return x_; }
float getPosY() const { return y_; }
float getVelX() const { return vx_; }
float getVelY() const { return vy_; }
float getAccelX() const { return ax_; }
float getAccelY() const { return ay_; }
// Establece la variable
void setVelX(float value) { vx_ = value; }
void setVelY(float value) { vy_ = value; }
void setAccelX(float value) { ax_ = value; }
void setAccelY(float value) { ay_ = value; }
// Establece el valor de la variable
void setFlip(SDL_RendererFlip flip) { flip_ = flip; }
// Gira el sprite horizontalmente
void flip() { flip_ = (flip_ == SDL_FLIP_HORIZONTAL) ? SDL_FLIP_NONE : SDL_FLIP_HORIZONTAL; }
// Obtiene el valor de la variable
SDL_RendererFlip getFlip() { return flip_; }
// Establece la posición y_ el tamaño del objeto
void setPos(SDL_Rect rect);
// Establece el valor de las variables
void setPos(float x, float y);
// Establece el valor de la variable
void setPosX(float value);
// Establece el valor de la variable
void setPosY(float value);
};

View File

@@ -0,0 +1,70 @@
#pragma once
#include <SDL3/SDL_rect.h> // Para SDL_Rect, SDL_Point
#include <SDL3/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr
class Surface; // lines 5-5
// Clase SSprite
class SSprite {
protected:
// Variables
std::shared_ptr<Surface> surface_; // Surface donde estan todos los dibujos del sprite
SDL_Rect pos_; // Posición y tamaño donde dibujar el sprite
SDL_Rect clip_; // Rectangulo de origen de la surface que se dibujará en pantalla
public:
// Constructor
SSprite(std::shared_ptr<Surface>, int x, int y, int w, int h);
SSprite(std::shared_ptr<Surface>, SDL_Rect rect);
explicit SSprite(std::shared_ptr<Surface>);
// Destructor
virtual ~SSprite() = default;
// Muestra el sprite por pantalla
virtual void render();
virtual void render(Uint8 source_color, Uint8 target_color);
// Reinicia las variables a cero
virtual void clear();
// Obtiene la posición y el tamaño
int getX() const { return pos_.x; }
int getY() const { return pos_.y; }
int getWidth() const { return pos_.w; }
int getHeight() const { return pos_.h; }
// Devuelve el rectangulo donde está el sprite
SDL_Rect getPosition() const { return pos_; }
SDL_Rect& getRect() { return pos_; }
// Establece la posición y el tamaño
void setX(int x) { pos_.x = x; }
void setY(int y) { pos_.y = y; }
void setWidth(int w) { pos_.w = w; }
void setHeight(int h) { pos_.h = h; }
// Establece la posición del objeto
void setPosition(int x, int y);
void setPosition(SDL_Point p);
void setPosition(SDL_Rect r) { pos_ = r; }
// Aumenta o disminuye la posición
void incX(int value) { pos_.x += value; }
void incY(int value) { pos_.y += value; }
// Obtiene el rectangulo que se dibuja de la surface
SDL_Rect getClip() const { return clip_; }
// Establece el rectangulo que se dibuja de la surface
void setClip(SDL_Rect rect) { clip_ = rect; }
void setClip(int x, int y, int w, int h) { clip_ = (SDL_Rect){x, y, w, h}; }
// Obtiene un puntero a la surface
std::shared_ptr<Surface> getSurface() const { return surface_; }
// Establece la surface a utilizar
void setSurface(std::shared_ptr<Surface> surface) { surface_ = surface; }
};

View File

@@ -1,9 +1,11 @@
// IWYU pragma: no_include <bits/std_abs.h> // IWYU pragma: no_include <bits/std_abs.h>
#include "surface.h" #include "surface.h"
#include <SDL2/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks #include <SDL3/SDL_error.h> // Para SDL_GetError
#include <cmath> // Para abs #include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para min, max, copy_n, fill #include <algorithm> // Para min, max, copy_n, fill
#include <cmath> // Para abs
#include <cstdint> // Para uint32_t #include <cstdint> // Para uint32_t
#include <cstring> // Para memcpy, size_t #include <cstring> // Para memcpy, size_t
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ist... #include <fstream> // Para basic_ifstream, basic_ostream, basic_ist...
@@ -12,16 +14,15 @@
#include <sstream> // Para basic_istringstream #include <sstream> // Para basic_istringstream
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <vector> // Para vector #include <vector> // Para vector
#include "gif.h" // Para Gif #include "gif.h" // Para Gif
#include "screen.h" // Para Screen #include "screen.h" // Para Screen
// Carga una paleta desde un archivo .gif // Carga una paleta desde un archivo .gif
Palette loadPalette(const std::string &file_path) Palette loadPalette(const std::string& file_path) {
{
// Abrir el archivo en modo binario // Abrir el archivo en modo binario
std::ifstream file(file_path, std::ios::binary | std::ios::ate); std::ifstream file(file_path, std::ios::binary | std::ios::ate);
if (!file.is_open()) if (!file.is_open()) {
{
throw std::runtime_error("Error opening file: " + file_path); throw std::runtime_error("Error opening file: " + file_path);
} }
@@ -30,16 +31,14 @@ Palette loadPalette(const std::string &file_path)
file.seekg(0, std::ios::beg); file.seekg(0, std::ios::beg);
std::vector<Uint8> buffer(size); std::vector<Uint8> buffer(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
{
throw std::runtime_error("Error reading file: " + file_path); throw std::runtime_error("Error reading file: " + file_path);
} }
// Cargar la paleta usando los datos del buffer // Cargar la paleta usando los datos del buffer
GIF::Gif gif; GIF::Gif gif;
std::vector<uint32_t> pal = gif.loadPalette(buffer.data()); std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
if (pal.empty()) if (pal.empty()) {
{
throw std::runtime_error("No palette found in GIF file: " + file_path); throw std::runtime_error("No palette found in GIF file: " + file_path);
} }
@@ -54,14 +53,12 @@ Palette loadPalette(const std::string &file_path)
} }
// Carga una paleta desde un archivo .pal // Carga una paleta desde un archivo .pal
Palette readPalFile(const std::string &file_path) Palette readPalFile(const std::string& file_path) {
{
Palette palette{}; Palette palette{};
palette.fill(0); // Inicializar todo con 0 (transparente por defecto) palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
std::ifstream file(file_path); std::ifstream file(file_path);
if (!file.is_open()) if (!file.is_open()) {
{
throw std::runtime_error("No se pudo abrir el archivo .pal"); throw std::runtime_error("No se pudo abrir el archivo .pal");
} }
@@ -69,28 +66,24 @@ Palette readPalFile(const std::string &file_path)
int line_number = 0; int line_number = 0;
int color_index = 0; int color_index = 0;
while (std::getline(file, line)) while (std::getline(file, line)) {
{
++line_number; ++line_number;
// Ignorar las tres primeras líneas del archivo // Ignorar las tres primeras líneas del archivo
if (line_number <= 3) if (line_number <= 3) {
{
continue; continue;
} }
// Procesar las líneas restantes con valores RGB // Procesar las líneas restantes con valores RGB
std::istringstream ss(line); std::istringstream ss(line);
int r, g, b; int r, g, b;
if (ss >> r >> g >> b) if (ss >> r >> g >> b) {
{
// Construir el color ARGB (A = 255 por defecto) // Construir el color ARGB (A = 255 por defecto)
Uint32 color = (255 << 24) | (r << 16) | (g << 8) | b; Uint32 color = (255 << 24) | (r << 16) | (g << 8) | b;
palette[color_index++] = color; palette[color_index++] = color;
// Limitar a un máximo de 256 colores (opcional) // Limitar a un máximo de 256 colores (opcional)
if (color_index >= 256) if (color_index >= 256) {
{
break; break;
} }
} }
@@ -105,9 +98,8 @@ Surface::Surface(int w, int h)
: surface_data_(std::make_shared<SurfaceData>(w, h)), : surface_data_(std::make_shared<SurfaceData>(w, h)),
transparent_color_(static_cast<Uint8>(PaletteColor::TRANSPARENT)) { initializeSubPalette(sub_palette_); } transparent_color_(static_cast<Uint8>(PaletteColor::TRANSPARENT)) { initializeSubPalette(sub_palette_); }
Surface::Surface(const std::string &file_path) Surface::Surface(const std::string& file_path)
: transparent_color_(static_cast<Uint8>(PaletteColor::TRANSPARENT)) : transparent_color_(static_cast<Uint8>(PaletteColor::TRANSPARENT)) {
{
SurfaceData loadedData = loadSurface(file_path); SurfaceData loadedData = loadSurface(file_path);
surface_data_ = std::make_shared<SurfaceData>(std::move(loadedData)); surface_data_ = std::make_shared<SurfaceData>(std::move(loadedData));
@@ -115,12 +107,10 @@ Surface::Surface(const std::string &file_path)
} }
// Carga una superficie desde un archivo // Carga una superficie desde un archivo
SurfaceData Surface::loadSurface(const std::string &file_path) SurfaceData Surface::loadSurface(const std::string& file_path) {
{
// Abrir el archivo usando std::ifstream para manejo automático del recurso // Abrir el archivo usando std::ifstream para manejo automático del recurso
std::ifstream file(file_path, std::ios::binary | std::ios::ate); std::ifstream file(file_path, std::ios::binary | std::ios::ate);
if (!file.is_open()) if (!file.is_open()) {
{
std::cerr << "Error opening file: " << file_path << std::endl; std::cerr << "Error opening file: " << file_path << std::endl;
throw std::runtime_error("Error opening file"); throw std::runtime_error("Error opening file");
} }
@@ -131,8 +121,7 @@ SurfaceData Surface::loadSurface(const std::string &file_path)
// Leer el contenido del archivo en un buffer // Leer el contenido del archivo en un buffer
std::vector<Uint8> buffer(size); std::vector<Uint8> buffer(size);
if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
{
std::cerr << "Error reading file: " << file_path << std::endl; std::cerr << "Error reading file: " << file_path << std::endl;
throw std::runtime_error("Error reading file"); throw std::runtime_error("Error reading file");
} }
@@ -141,8 +130,7 @@ SurfaceData Surface::loadSurface(const std::string &file_path)
GIF::Gif gif; GIF::Gif gif;
Uint16 w = 0, h = 0; Uint16 w = 0, h = 0;
std::vector<Uint8> rawPixels = gif.loadGif(buffer.data(), w, h); std::vector<Uint8> rawPixels = gif.loadGif(buffer.data(), w, h);
if (rawPixels.empty()) if (rawPixels.empty()) {
{
std::cerr << "Error loading GIF from file: " << file_path << std::endl; std::cerr << "Error loading GIF from file: " << file_path << std::endl;
throw std::runtime_error("Error loading GIF"); throw std::runtime_error("Error loading GIF");
} }
@@ -159,36 +147,30 @@ SurfaceData Surface::loadSurface(const std::string &file_path)
} }
// Carga una paleta desde un archivo // Carga una paleta desde un archivo
void Surface::loadPalette(const std::string &file_path) void Surface::loadPalette(const std::string& file_path) {
{
palette_ = ::loadPalette(file_path); palette_ = ::loadPalette(file_path);
} }
// Carga una paleta desde otra paleta // Carga una paleta desde otra paleta
void Surface::loadPalette(Palette palette) void Surface::loadPalette(Palette palette) {
{
palette_ = palette; palette_ = palette;
} }
// Establece un color en la paleta // Establece un color en la paleta
void Surface::setColor(int index, Uint32 color) void Surface::setColor(int index, Uint32 color) {
{
palette_.at(index) = color; palette_.at(index) = color;
} }
// Rellena la superficie con un color // Rellena la superficie con un color
void Surface::clear(Uint8 color) void Surface::clear(Uint8 color) {
{
const size_t total_pixels = surface_data_->width * surface_data_->height; const size_t total_pixels = surface_data_->width * surface_data_->height;
Uint8 *data_ptr = surface_data_->data.get(); Uint8* data_ptr = surface_data_->data.get();
std::fill(data_ptr, data_ptr + total_pixels, color); std::fill(data_ptr, data_ptr + total_pixels, color);
} }
// Pone un pixel en la SurfaceData // Pone un pixel en la SurfaceData
void Surface::putPixel(int x, int y, Uint8 color) void Surface::putPixel(int x, int y, Uint8 color) {
{ if (x < 0 || y < 0 || x >= surface_data_->width || y >= surface_data_->height) {
if (x < 0 || y < 0 || x >= surface_data_->width || y >= surface_data_->height)
{
return; // Coordenadas fuera de rango return; // Coordenadas fuera de rango
} }
@@ -200,8 +182,7 @@ void Surface::putPixel(int x, int y, Uint8 color)
Uint8 Surface::getPixel(int x, int y) { return surface_data_->data.get()[x + y * surface_data_->width]; } Uint8 Surface::getPixel(int x, int y) { return surface_data_->data.get()[x + y * surface_data_->width]; }
// Dibuja un rectangulo relleno // Dibuja un rectangulo relleno
void Surface::fillRect(const SDL_Rect *rect, Uint8 color) void Surface::fillRect(const SDL_Rect* rect, Uint8 color) {
{
// Limitar los valores del rectángulo al tamaño de la superficie // Limitar los valores del rectángulo al tamaño de la superficie
int x_start = std::max(0, rect->x); int x_start = std::max(0, rect->x);
int y_start = std::max(0, rect->y); int y_start = std::max(0, rect->y);
@@ -209,10 +190,8 @@ void Surface::fillRect(const SDL_Rect *rect, Uint8 color)
int y_end = std::min(rect->y + rect->h, static_cast<int>(surface_data_->height)); int y_end = std::min(rect->y + rect->h, static_cast<int>(surface_data_->height));
// Recorrer cada píxel dentro del rectángulo directamente // Recorrer cada píxel dentro del rectángulo directamente
for (int y = y_start; y < y_end; ++y) for (int y = y_start; y < y_end; ++y) {
{ for (int x = x_start; x < x_end; ++x) {
for (int x = x_start; x < x_end; ++x)
{
const int index = x + y * surface_data_->width; const int index = x + y * surface_data_->width;
surface_data_->data.get()[index] = color; surface_data_->data.get()[index] = color;
} }
@@ -220,8 +199,7 @@ void Surface::fillRect(const SDL_Rect *rect, Uint8 color)
} }
// Dibuja el borde de un rectangulo // Dibuja el borde de un rectangulo
void Surface::drawRectBorder(const SDL_Rect *rect, Uint8 color) void Surface::drawRectBorder(const SDL_Rect* rect, Uint8 color) {
{
// Limitar los valores del rectángulo al tamaño de la superficie // Limitar los valores del rectángulo al tamaño de la superficie
int x_start = std::max(0, rect->x); int x_start = std::max(0, rect->x);
int y_start = std::max(0, rect->y); int y_start = std::max(0, rect->y);
@@ -229,8 +207,7 @@ void Surface::drawRectBorder(const SDL_Rect *rect, Uint8 color)
int y_end = std::min(rect->y + rect->h, static_cast<int>(surface_data_->height)); int y_end = std::min(rect->y + rect->h, static_cast<int>(surface_data_->height));
// Dibujar bordes horizontales // Dibujar bordes horizontales
for (int x = x_start; x < x_end; ++x) for (int x = x_start; x < x_end; ++x) {
{
// Borde superior // Borde superior
const int top_index = x + y_start * surface_data_->width; const int top_index = x + y_start * surface_data_->width;
surface_data_->data.get()[top_index] = color; surface_data_->data.get()[top_index] = color;
@@ -241,8 +218,7 @@ void Surface::drawRectBorder(const SDL_Rect *rect, Uint8 color)
} }
// Dibujar bordes verticales // Dibujar bordes verticales
for (int y = y_start; y < y_end; ++y) for (int y = y_start; y < y_end; ++y) {
{
// Borde izquierdo // Borde izquierdo
const int left_index = x_start + y * surface_data_->width; const int left_index = x_start + y * surface_data_->width;
surface_data_->data.get()[left_index] = color; surface_data_->data.get()[left_index] = color;
@@ -254,8 +230,7 @@ void Surface::drawRectBorder(const SDL_Rect *rect, Uint8 color)
} }
// Dibuja una linea // Dibuja una linea
void Surface::drawLine(int x1, int y1, int x2, int y2, Uint8 color) void Surface::drawLine(int x1, int y1, int x2, int y2, Uint8 color) {
{
// Calcula las diferencias // Calcula las diferencias
int dx = std::abs(x2 - x1); int dx = std::abs(x2 - x1);
int dy = std::abs(y2 - y1); int dy = std::abs(y2 - y1);
@@ -266,11 +241,9 @@ void Surface::drawLine(int x1, int y1, int x2, int y2, Uint8 color)
int err = dx - dy; int err = dx - dy;
while (true) while (true) {
{
// Asegúrate de no dibujar fuera de los límites de la superficie // Asegúrate de no dibujar fuera de los límites de la superficie
if (x1 >= 0 && x1 < surface_data_->width && y1 >= 0 && y1 < surface_data_->height) if (x1 >= 0 && x1 < surface_data_->width && y1 >= 0 && y1 < surface_data_->height) {
{
surface_data_->data.get()[x1 + y1 * surface_data_->width] = color; surface_data_->data.get()[x1 + y1 * surface_data_->width] = color;
} }
@@ -279,21 +252,18 @@ void Surface::drawLine(int x1, int y1, int x2, int y2, Uint8 color)
break; break;
int e2 = 2 * err; int e2 = 2 * err;
if (e2 > -dy) if (e2 > -dy) {
{
err -= dy; err -= dy;
x1 += sx; x1 += sx;
} }
if (e2 < dx) if (e2 < dx) {
{
err += dx; err += dx;
y1 += sy; y1 += sy;
} }
} }
} }
void Surface::render(int dx, int dy, int sx, int sy, int w, int h) void Surface::render(int dx, int dy, int sx, int sy, int w, int h) {
{
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Limitar la región para evitar accesos fuera de rango en origen // Limitar la región para evitar accesos fuera de rango en origen
@@ -304,21 +274,16 @@ void Surface::render(int dx, int dy, int sx, int sy, int w, int h)
w = std::min(w, surface_data->width - dx); w = std::min(w, surface_data->width - dx);
h = std::min(h, surface_data->height - dy); h = std::min(h, surface_data->height - dy);
for (int iy = 0; iy < h; ++iy) for (int iy = 0; iy < h; ++iy) {
{ for (int ix = 0; ix < w; ++ix) {
for (int ix = 0; ix < w; ++ix)
{
// Verificar que las coordenadas de destino están dentro de los límites // Verificar que las coordenadas de destino están dentro de los límites
if (int dest_x = dx + ix; dest_x >= 0 && dest_x < surface_data->width) if (int dest_x = dx + ix; dest_x >= 0 && dest_x < surface_data->width) {
{ if (int dest_y = dy + iy; dest_y >= 0 && dest_y < surface_data->height) {
if (int dest_y = dy + iy; dest_y >= 0 && dest_y < surface_data->height)
{
int src_x = sx + ix; int src_x = sx + ix;
int src_y = sy + iy; int src_y = sy + iy;
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width]; Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
if (color != transparent_color_) if (color != transparent_color_) {
{
surface_data->data.get()[dest_x + dest_y * surface_data->width] = sub_palette_[color]; surface_data->data.get()[dest_x + dest_y * surface_data->width] = sub_palette_[color];
} }
} }
@@ -327,8 +292,7 @@ void Surface::render(int dx, int dy, int sx, int sy, int w, int h)
} }
} }
void Surface::render(int x, int y, SDL_Rect *srcRect, SDL_RendererFlip flip) void Surface::render(int x, int y, SDL_Rect* srcRect, SDL_RendererFlip flip) {
{
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
// Determina la región de origen (clip) a renderizar // Determina la región de origen (clip) a renderizar
@@ -348,10 +312,8 @@ void Surface::render(int x, int y, SDL_Rect *srcRect, SDL_RendererFlip flip)
h = std::min(h, surface_data_dest->height - y); h = std::min(h, surface_data_dest->height - y);
// Renderiza píxel por píxel aplicando el flip si es necesario // Renderiza píxel por píxel aplicando el flip si es necesario
for (int iy = 0; iy < h; ++iy) for (int iy = 0; iy < h; ++iy) {
{ for (int ix = 0; ix < w; ++ix) {
for (int ix = 0; ix < w; ++ix)
{
// Coordenadas de origen // Coordenadas de origen
int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix); int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix);
int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy); int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy);
@@ -361,12 +323,10 @@ void Surface::render(int x, int y, SDL_Rect *srcRect, SDL_RendererFlip flip)
int dest_y = y + iy; int dest_y = y + iy;
// Verificar que las coordenadas de destino están dentro de los límites // Verificar que las coordenadas de destino están dentro de los límites
if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height) if (dest_x >= 0 && dest_x < surface_data_dest->width && dest_y >= 0 && dest_y < surface_data_dest->height) {
{
// Copia el píxel si no es transparente // Copia el píxel si no es transparente
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width]; Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
if (color != transparent_color_) if (color != transparent_color_) {
{
surface_data_dest->data[dest_x + dest_y * surface_data_dest->width] = sub_palette_[color]; surface_data_dest->data[dest_x + dest_y * surface_data_dest->width] = sub_palette_[color];
} }
} }
@@ -375,8 +335,7 @@ void Surface::render(int x, int y, SDL_Rect *srcRect, SDL_RendererFlip flip)
} }
// Copia una región de la superficie de origen a la de destino // Copia una región de la superficie de origen a la de destino
void Surface::render(SDL_Rect *srcRect, SDL_Rect *dstRect, SDL_RendererFlip flip) void Surface::render(SDL_Rect* srcRect, SDL_Rect* dstRect, SDL_RendererFlip flip) {
{
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Si srcRect es nullptr, tomar toda la superficie fuente // Si srcRect es nullptr, tomar toda la superficie fuente
@@ -392,8 +351,7 @@ void Surface::render(SDL_Rect *srcRect, SDL_Rect *dstRect, SDL_RendererFlip flip
int dh = (dstRect) ? dstRect->h : sh; int dh = (dstRect) ? dstRect->h : sh;
// Asegurarse de que srcRect y dstRect tienen las mismas dimensiones // Asegurarse de que srcRect y dstRect tienen las mismas dimensiones
if (sw != dw || sh != dh) if (sw != dw || sh != dh) {
{
dw = sw; // Respetar las dimensiones de srcRect dw = sw; // Respetar las dimensiones de srcRect
dh = sh; dh = sh;
} }
@@ -408,23 +366,18 @@ void Surface::render(SDL_Rect *srcRect, SDL_Rect *dstRect, SDL_RendererFlip flip
int final_height = std::min(sh, dh); int final_height = std::min(sh, dh);
// Renderiza píxel por píxel aplicando el flip si es necesario // Renderiza píxel por píxel aplicando el flip si es necesario
for (int iy = 0; iy < final_height; ++iy) for (int iy = 0; iy < final_height; ++iy) {
{ for (int ix = 0; ix < final_width; ++ix) {
for (int ix = 0; ix < final_width; ++ix)
{
// Coordenadas de origen // Coordenadas de origen
int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + final_width - 1 - ix) : (sx + ix); int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + final_width - 1 - ix) : (sx + ix);
int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + final_height - 1 - iy) : (sy + iy); int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + final_height - 1 - iy) : (sy + iy);
// Coordenadas de destino // Coordenadas de destino
if (int dest_x = dx + ix; dest_x >= 0 && dest_x < surface_data->width) if (int dest_x = dx + ix; dest_x >= 0 && dest_x < surface_data->width) {
{ if (int dest_y = dy + iy; dest_y >= 0 && dest_y < surface_data->height) {
if (int dest_y = dy + iy; dest_y >= 0 && dest_y < surface_data->height)
{
// Copiar el píxel si no es transparente // Copiar el píxel si no es transparente
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width]; Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
if (color != transparent_color_) if (color != transparent_color_) {
{
surface_data->data[dest_x + dest_y * surface_data->width] = sub_palette_[color]; surface_data->data[dest_x + dest_y * surface_data->width] = sub_palette_[color];
} }
} }
@@ -434,8 +387,7 @@ void Surface::render(SDL_Rect *srcRect, SDL_Rect *dstRect, SDL_RendererFlip flip
} }
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro // Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 target_color, SDL_Rect *srcRect, SDL_RendererFlip flip) void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 target_color, SDL_Rect* srcRect, SDL_RendererFlip flip) {
{
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData(); auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
// Determina la región de origen (clip) a renderizar // Determina la región de origen (clip) a renderizar
@@ -449,10 +401,8 @@ void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 tar
h = std::min(h, surface_data_->height - sy); h = std::min(h, surface_data_->height - sy);
// Renderiza píxel por píxel aplicando el flip si es necesario // Renderiza píxel por píxel aplicando el flip si es necesario
for (int iy = 0; iy < h; ++iy) for (int iy = 0; iy < h; ++iy) {
{ for (int ix = 0; ix < w; ++ix) {
for (int ix = 0; ix < w; ++ix)
{
// Coordenadas de origen // Coordenadas de origen
int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix); int src_x = (flip == SDL_FLIP_HORIZONTAL) ? (sx + w - 1 - ix) : (sx + ix);
int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy); int src_y = (flip == SDL_FLIP_VERTICAL) ? (sy + h - 1 - iy) : (sy + iy);
@@ -462,15 +412,13 @@ void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 tar
int dest_y = y + iy; int dest_y = y + iy;
// Verifica que las coordenadas de destino estén dentro de los límites // Verifica que las coordenadas de destino estén dentro de los límites
if (dest_x < 0 || dest_y < 0 || dest_x >= surface_data->width || dest_y >= surface_data->height) if (dest_x < 0 || dest_y < 0 || dest_x >= surface_data->width || dest_y >= surface_data->height) {
{
continue; // Saltar píxeles fuera del rango del destino continue; // Saltar píxeles fuera del rango del destino
} }
// Copia el píxel si no es transparente // Copia el píxel si no es transparente
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width]; Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
if (color != transparent_color_) if (color != transparent_color_) {
{
surface_data->data[dest_x + dest_y * surface_data->width] = surface_data->data[dest_x + dest_y * surface_data->width] =
(color == source_color) ? target_color : color; (color == source_color) ? target_color : color;
} }
@@ -479,34 +427,28 @@ void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 tar
} }
// Vuelca la superficie a una textura // Vuelca la superficie a una textura
void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture) void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) {
{ if (!renderer || !texture || !surface_data_) {
if (!renderer || !texture || !surface_data_)
{
throw std::runtime_error("Renderer or texture is null."); throw std::runtime_error("Renderer or texture is null.");
} }
if (surface_data_->width <= 0 || surface_data_->height <= 0 || !surface_data_->data.get()) if (surface_data_->width <= 0 || surface_data_->height <= 0 || !surface_data_->data.get()) {
{
throw std::runtime_error("Invalid surface dimensions or data."); throw std::runtime_error("Invalid surface dimensions or data.");
} }
Uint32 *pixels = nullptr; Uint32* pixels = nullptr;
int pitch = 0; int pitch = 0;
// Bloquea la textura para modificar los píxeles directamente // Bloquea la textura para modificar los píxeles directamente
if (SDL_LockTexture(texture, nullptr, reinterpret_cast<void **>(&pixels), &pitch) != 0) if (SDL_LockTexture(texture, nullptr, reinterpret_cast<void**>(&pixels), &pitch) != 0) {
{
throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError())); throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError()));
} }
// Convertir `pitch` de bytes a Uint32 (asegurando alineación correcta en hardware) // Convertir `pitch` de bytes a Uint32 (asegurando alineación correcta en hardware)
int row_stride = pitch / sizeof(Uint32); int row_stride = pitch / sizeof(Uint32);
for (int y = 0; y < surface_data_->height; ++y) for (int y = 0; y < surface_data_->height; ++y) {
{ for (int x = 0; x < surface_data_->width; ++x) {
for (int x = 0; x < surface_data_->width; ++x)
{
// Calcular la posición correcta en la textura teniendo en cuenta el stride // Calcular la posición correcta en la textura teniendo en cuenta el stride
int texture_index = y * row_stride + x; int texture_index = y * row_stride + x;
int surface_index = y * surface_data_->width + x; int surface_index = y * surface_data_->width + x;
@@ -518,39 +460,32 @@ void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture)
SDL_UnlockTexture(texture); // Desbloquea la textura SDL_UnlockTexture(texture); // Desbloquea la textura
// Renderiza la textura en la pantalla completa // Renderiza la textura en la pantalla completa
if (SDL_RenderCopy(renderer, texture, nullptr, nullptr) != 0) if (SDL_RenderCopy(renderer, texture, nullptr, nullptr) != 0) {
{
throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError())); throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError()));
} }
} }
// Vuelca la superficie a una textura // Vuelca la superficie a una textura
void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect *srcRect, SDL_Rect *destRect) void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect* srcRect, SDL_Rect* destRect) {
{ if (!renderer || !texture || !surface_data_) {
if (!renderer || !texture || !surface_data_)
{
throw std::runtime_error("Renderer or texture is null."); throw std::runtime_error("Renderer or texture is null.");
} }
if (surface_data_->width <= 0 || surface_data_->height <= 0 || !surface_data_->data.get()) if (surface_data_->width <= 0 || surface_data_->height <= 0 || !surface_data_->data.get()) {
{
throw std::runtime_error("Invalid surface dimensions or data."); throw std::runtime_error("Invalid surface dimensions or data.");
} }
Uint32 *pixels = nullptr; Uint32* pixels = nullptr;
int pitch = 0; int pitch = 0;
if (SDL_LockTexture(texture, destRect, reinterpret_cast<void **>(&pixels), &pitch) != 0) if (SDL_LockTexture(texture, destRect, reinterpret_cast<void**>(&pixels), &pitch) != 0) {
{
throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError())); throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError()));
} }
int row_stride = pitch / sizeof(Uint32); int row_stride = pitch / sizeof(Uint32);
for (int y = 0; y < surface_data_->height; ++y) for (int y = 0; y < surface_data_->height; ++y) {
{ for (int x = 0; x < surface_data_->width; ++x) {
for (int x = 0; x < surface_data_->width; ++x)
{
int texture_index = y * row_stride + x; int texture_index = y * row_stride + x;
int surface_index = y * surface_data_->width + x; int surface_index = y * surface_data_->width + x;
@@ -561,25 +496,21 @@ void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Re
SDL_UnlockTexture(texture); SDL_UnlockTexture(texture);
// Renderiza la textura con los rectángulos especificados // Renderiza la textura con los rectángulos especificados
if (SDL_RenderCopy(renderer, texture, srcRect, destRect) != 0) if (SDL_RenderCopy(renderer, texture, srcRect, destRect) != 0) {
{
throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError())); throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError()));
} }
} }
// Realiza un efecto de fundido en la paleta principal // Realiza un efecto de fundido en la paleta principal
bool Surface::fadePalette() bool Surface::fadePalette() {
{
// Verificar que el tamaño mínimo de palette_ sea adecuado // Verificar que el tamaño mínimo de palette_ sea adecuado
static constexpr int palette_size = 19; static constexpr int palette_size = 19;
if (sizeof(palette_) / sizeof(palette_[0]) < palette_size) if (sizeof(palette_) / sizeof(palette_[0]) < palette_size) {
{
throw std::runtime_error("Palette size is insufficient for fadePalette operation."); throw std::runtime_error("Palette size is insufficient for fadePalette operation.");
} }
// Desplazar colores (pares e impares) // Desplazar colores (pares e impares)
for (int i = 18; i > 1; --i) for (int i = 18; i > 1; --i) {
{
palette_[i] = palette_[i - 2]; palette_[i] = palette_[i - 2];
} }
@@ -591,8 +522,7 @@ bool Surface::fadePalette()
} }
// Realiza un efecto de fundido en la paleta secundaria // Realiza un efecto de fundido en la paleta secundaria
bool Surface::fadeSubPalette(Uint32 delay) bool Surface::fadeSubPalette(Uint32 delay) {
{
// Variable estática para almacenar el último tick // Variable estática para almacenar el último tick
static Uint32 last_tick = 0; static Uint32 last_tick = 0;
@@ -600,8 +530,7 @@ bool Surface::fadeSubPalette(Uint32 delay)
Uint32 current_tick = SDL_GetTicks(); Uint32 current_tick = SDL_GetTicks();
// Verificar si ha pasado el tiempo de retardo // Verificar si ha pasado el tiempo de retardo
if (current_tick - last_tick < delay) if (current_tick - last_tick < delay) {
{
return false; // No se realiza el fade return false; // No se realiza el fade
} }
@@ -610,14 +539,12 @@ bool Surface::fadeSubPalette(Uint32 delay)
// Verificar que el tamaño mínimo de sub_palette_ sea adecuado // Verificar que el tamaño mínimo de sub_palette_ sea adecuado
static constexpr int sub_palette_size = 19; static constexpr int sub_palette_size = 19;
if (sizeof(sub_palette_) / sizeof(sub_palette_[0]) < sub_palette_size) if (sizeof(sub_palette_) / sizeof(sub_palette_[0]) < sub_palette_size) {
{
throw std::runtime_error("Palette size is insufficient for fadePalette operation."); throw std::runtime_error("Palette size is insufficient for fadePalette operation.");
} }
// Desplazar colores (pares e impares) // Desplazar colores (pares e impares)
for (int i = 18; i > 1; --i) for (int i = 18; i > 1; --i) {
{
sub_palette_[i] = sub_palette_[i - 2]; sub_palette_[i] = sub_palette_[i - 2];
} }

View File

@@ -1,13 +1,15 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect #include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_render.h> // Para SDL_FLIP_NONE, SDL_RendererFlip, SDL_Re... #include <SDL3/SDL_render.h> // Para SDL_FLIP_NONE, SDL_RendererFlip, SDL_Re...
#include <SDL2/SDL_stdinc.h> // Para Uint8, Uint16, Uint32 #include <SDL3/SDL_stdinc.h> // Para Uint8, Uint16, Uint32
#include <array> // Para array #include <array> // Para array
#include <memory> // Para default_delete, shared_ptr, __shared_pt... #include <memory> // Para default_delete, shared_ptr, __shared_pt...
#include <numeric> // Para iota #include <numeric> // Para iota
#include <string> // Para string #include <string> // Para string
#include <utility> // Para move #include <utility> // Para move
#include "utils.h" // Para PaletteColor #include "utils.h" // Para PaletteColor
// Alias // Alias
@@ -15,69 +17,74 @@ using Palette = std::array<Uint32, 256>;
using SubPalette = std::array<Uint8, 256>; using SubPalette = std::array<Uint8, 256>;
// Carga una paleta desde un archivo .gif // Carga una paleta desde un archivo .gif
Palette loadPalette(const std::string &file_path); Palette loadPalette(const std::string& file_path);
// Carga una paleta desde un archivo .pal // Carga una paleta desde un archivo .pal
Palette readPalFile(const std::string &file_path); Palette readPalFile(const std::string& file_path);
struct SurfaceData struct SurfaceData {
{
std::shared_ptr<Uint8[]> data; // Usa std::shared_ptr para gestión automática std::shared_ptr<Uint8[]> data; // Usa std::shared_ptr para gestión automática
Uint16 width; // Ancho de la imagen Uint16 width; // Ancho de la imagen
Uint16 height; // Alto de la imagen Uint16 height; // Alto de la imagen
// Constructor por defecto // Constructor por defecto
SurfaceData() : data(nullptr), width(0), height(0) {} SurfaceData()
: data(nullptr),
width(0),
height(0) {}
// Constructor que inicializa dimensiones y asigna memoria // Constructor que inicializa dimensiones y asigna memoria
SurfaceData(Uint16 w, Uint16 h) SurfaceData(Uint16 w, Uint16 h)
: data(std::shared_ptr<Uint8[]>(new Uint8[w * h](), std::default_delete<Uint8[]>())), width(w), height(h) {} : data(std::shared_ptr<Uint8[]>(new Uint8[w * h](), std::default_delete<Uint8[]>())),
width(w),
height(h) {}
// Constructor para inicializar directamente con datos // Constructor para inicializar directamente con datos
SurfaceData(Uint16 w, Uint16 h, std::shared_ptr<Uint8[]> pixels) SurfaceData(Uint16 w, Uint16 h, std::shared_ptr<Uint8[]> pixels)
: data(std::move(pixels)), width(w), height(h) {} : data(std::move(pixels)),
width(w),
height(h) {}
// Constructor de movimiento // Constructor de movimiento
SurfaceData(SurfaceData &&other) noexcept = default; SurfaceData(SurfaceData&& other) noexcept = default;
// Operador de movimiento // Operador de movimiento
SurfaceData &operator=(SurfaceData &&other) noexcept = default; SurfaceData& operator=(SurfaceData&& other) noexcept = default;
// Evita copias accidentales // Evita copias accidentales
SurfaceData(const SurfaceData &) = delete; SurfaceData(const SurfaceData&) = delete;
SurfaceData &operator=(const SurfaceData &) = delete; SurfaceData& operator=(const SurfaceData&) = delete;
}; };
class Surface class Surface {
{ private:
private:
std::shared_ptr<SurfaceData> surface_data_; // Datos a dibujar std::shared_ptr<SurfaceData> surface_data_; // Datos a dibujar
Palette palette_; // Paleta para volcar la SurfaceData a una Textura Palette palette_; // Paleta para volcar la SurfaceData a una Textura
SubPalette sub_palette_; // Paleta para reindexar colores SubPalette sub_palette_; // Paleta para reindexar colores
int transparent_color_; // Indice de la paleta que se omite en la copia de datos int transparent_color_; // Indice de la paleta que se omite en la copia de datos
public: public:
// Constructor // Constructor
Surface(int w, int h); Surface(int w, int h);
explicit Surface(const std::string &file_path); explicit Surface(const std::string& file_path);
// Destructor // Destructor
~Surface() = default; ~Surface() = default;
// Carga una SurfaceData desde un archivo // Carga una SurfaceData desde un archivo
SurfaceData loadSurface(const std::string &file_path); SurfaceData loadSurface(const std::string& file_path);
// Carga una paleta desde un archivo // Carga una paleta desde un archivo
void loadPalette(const std::string &file_path); void loadPalette(const std::string& file_path);
void loadPalette(Palette palette); void loadPalette(Palette palette);
// Copia una región de la SurfaceData de origen a la SurfaceData de destino // Copia una región de la SurfaceData de origen a la SurfaceData de destino
void render(int dx, int dy, int sx, int sy, int w, int h); void render(int dx, int dy, int sx, int sy, int w, int h);
void render(int x, int y, SDL_Rect *clip = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE); void render(int x, int y, SDL_Rect* clip = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE);
void render(SDL_Rect *srcRect = nullptr, SDL_Rect *dstRect = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE); void render(SDL_Rect* srcRect = nullptr, SDL_Rect* dstRect = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE);
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro // Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_Rect *srcRect = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE); void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_Rect* srcRect = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE);
// Establece un color en la paleta // Establece un color en la paleta
void setColor(int index, Uint32 color); void setColor(int index, Uint32 color);
@@ -86,8 +93,8 @@ public:
void clear(Uint8 color); void clear(Uint8 color);
// Vuelca la SurfaceData a una textura // Vuelca la SurfaceData a una textura
void copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture); void copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture);
void copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_Rect *srcRect, SDL_Rect *destRect); void copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture, SDL_Rect* srcRect, SDL_Rect* destRect);
// Realiza un efecto de fundido en las paletas // Realiza un efecto de fundido en las paletas
bool fadePalette(); bool fadePalette();
@@ -100,10 +107,10 @@ public:
Uint8 getPixel(int x, int y); Uint8 getPixel(int x, int y);
// Dibuja un rectangulo relleno // Dibuja un rectangulo relleno
void fillRect(const SDL_Rect *rect, Uint8 color); void fillRect(const SDL_Rect* rect, Uint8 color);
// Dibuja el borde de un rectangulo // Dibuja el borde de un rectangulo
void drawRectBorder(const SDL_Rect *rect, Uint8 color); void drawRectBorder(const SDL_Rect* rect, Uint8 color);
// Dibuja una linea // Dibuja una linea
void drawLine(int x1, int y1, int x2, int y2, Uint8 color); void drawLine(int x1, int y1, int x2, int y2, Uint8 color);
@@ -121,8 +128,8 @@ public:
void setTransparentColor(Uint8 color = static_cast<Uint8>(PaletteColor::TRANSPARENT)) { transparent_color_ = color; } void setTransparentColor(Uint8 color = static_cast<Uint8>(PaletteColor::TRANSPARENT)) { transparent_color_ = color; }
// Paleta // Paleta
void setPalette(const std::array<Uint32, 256> &palette) { palette_ = palette; } void setPalette(const std::array<Uint32, 256>& palette) { palette_ = palette; }
// Inicializa la sub paleta // Inicializa la sub paleta
void initializeSubPalette(SubPalette &palette) { std::iota(palette.begin(), palette.end(), 0); } void initializeSubPalette(SubPalette& palette) { std::iota(palette.begin(), palette.end(), 0); }
}; };

View File

@@ -1,22 +1,23 @@
#include "text.h" #include "text.h"
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <stddef.h> // Para size_t #include <stddef.h> // Para size_t
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream #include <fstream> // Para basic_ifstream, basic_istream, basic_ostream
#include <iostream> // Para cerr #include <iostream> // Para cerr
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include "s_sprite.h" // Para SSprite #include "s_sprite.h" // Para SSprite
#include "screen.h" // Para Screen #include "screen.h" // Para Screen
#include "surface.h" // Para Surface #include "surface.h" // Para Surface
#include "utils.h" // Para getFileName, stringToColor, printWithDots #include "utils.h" // Para getFileName, stringToColor, printWithDots
// Llena una estructuta TextFile desde un fichero // Llena una estructuta TextFile desde un fichero
std::shared_ptr<TextFile> loadTextFile(const std::string &file_path) std::shared_ptr<TextFile> loadTextFile(const std::string& file_path) {
{
auto tf = std::make_shared<TextFile>(); auto tf = std::make_shared<TextFile>();
// Inicializa a cero el vector con las coordenadas // Inicializa a cero el vector con las coordenadas
for (int i = 0; i < 128; ++i) for (int i = 0; i < 128; ++i) {
{
tf->offset[i].x = 0; tf->offset[i].x = 0;
tf->offset[i].y = 0; tf->offset[i].y = 0;
tf->offset[i].w = 0; tf->offset[i].w = 0;
@@ -27,8 +28,7 @@ std::shared_ptr<TextFile> loadTextFile(const std::string &file_path)
// Abre el fichero para leer los valores // Abre el fichero para leer los valores
std::ifstream file(file_path); std::ifstream file(file_path);
if (file.is_open() && file.good()) if (file.is_open() && file.good()) {
{
std::string buffer; std::string buffer;
// Lee los dos primeros valores del fichero // Lee los dos primeros valores del fichero
@@ -43,8 +43,7 @@ std::shared_ptr<TextFile> loadTextFile(const std::string &file_path)
// lee el resto de datos del fichero // lee el resto de datos del fichero
auto index = 32; auto index = 32;
auto line_read = 0; auto line_read = 0;
while (std::getline(file, buffer)) while (std::getline(file, buffer)) {
{
// Almacena solo las lineas impares // Almacena solo las lineas impares
if (line_read % 2 == 1) if (line_read % 2 == 1)
tf->offset[index++].w = std::stoi(buffer); tf->offset[index++].w = std::stoi(buffer);
@@ -60,15 +59,13 @@ std::shared_ptr<TextFile> loadTextFile(const std::string &file_path)
} }
// El fichero no se puede abrir // El fichero no se puede abrir
else else {
{
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl; std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl;
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path)); throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
} }
// Establece las coordenadas para cada caracter ascii de la cadena y su ancho // Establece las coordenadas para cada caracter ascii de la cadena y su ancho
for (int i = 32; i < 128; ++i) for (int i = 32; i < 128; ++i) {
{
tf->offset[i].x = ((i - 32) % 15) * tf->box_width; tf->offset[i].x = ((i - 32) % 15) * tf->box_width;
tf->offset[i].y = ((i - 32) / 15) * tf->box_height; tf->offset[i].y = ((i - 32) / 15) * tf->box_height;
} }
@@ -77,16 +74,14 @@ std::shared_ptr<TextFile> loadTextFile(const std::string &file_path)
} }
// Constructor // Constructor
Text::Text(std::shared_ptr<Surface> surface, const std::string &text_file) Text::Text(std::shared_ptr<Surface> surface, const std::string& text_file) {
{
// Carga los offsets desde el fichero // Carga los offsets desde el fichero
auto tf = loadTextFile(text_file); auto tf = loadTextFile(text_file);
// Inicializa variables desde la estructura // Inicializa variables desde la estructura
box_height_ = tf->box_height; box_height_ = tf->box_height;
box_width_ = tf->box_width; box_width_ = tf->box_width;
for (int i = 0; i < 128; ++i) for (int i = 0; i < 128; ++i) {
{
offset_[i].x = tf->offset[i].x; offset_[i].x = tf->offset[i].x;
offset_[i].y = tf->offset[i].y; offset_[i].y = tf->offset[i].y;
offset_[i].w = tf->offset[i].w; offset_[i].w = tf->offset[i].w;
@@ -100,13 +95,11 @@ Text::Text(std::shared_ptr<Surface> surface, const std::string &text_file)
} }
// Constructor // Constructor
Text::Text(std::shared_ptr<Surface> surface, std::shared_ptr<TextFile> text_file) Text::Text(std::shared_ptr<Surface> surface, std::shared_ptr<TextFile> text_file) {
{
// Inicializa variables desde la estructura // Inicializa variables desde la estructura
box_height_ = text_file->box_height; box_height_ = text_file->box_height;
box_width_ = text_file->box_width; box_width_ = text_file->box_width;
for (int i = 0; i < 128; ++i) for (int i = 0; i < 128; ++i) {
{
offset_[i].x = text_file->offset[i].x; offset_[i].x = text_file->offset[i].x;
offset_[i].y = text_file->offset[i].y; offset_[i].y = text_file->offset[i].y;
offset_[i].w = text_file->offset[i].w; offset_[i].w = text_file->offset[i].w;
@@ -120,16 +113,14 @@ Text::Text(std::shared_ptr<Surface> surface, std::shared_ptr<TextFile> text_file
} }
// Escribe texto en pantalla // Escribe texto en pantalla
void Text::write(int x, int y, const std::string &text, int kerning, int lenght) void Text::write(int x, int y, const std::string& text, int kerning, int lenght) {
{
int shift = 0; int shift = 0;
if (lenght == -1) if (lenght == -1)
lenght = text.length(); lenght = text.length();
sprite_->setY(y); sprite_->setY(y);
for (int i = 0; i < lenght; ++i) for (int i = 0; i < lenght; ++i) {
{
auto index = static_cast<int>(text[i]); auto index = static_cast<int>(text[i]);
sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_); sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
sprite_->setX(x + shift); sprite_->setX(x + shift);
@@ -139,8 +130,7 @@ void Text::write(int x, int y, const std::string &text, int kerning, int lenght)
} }
// Escribe el texto en una surface // Escribe el texto en una surface
std::shared_ptr<Surface> Text::writeToSurface(const std::string &text, int zoom, int kerning) std::shared_ptr<Surface> Text::writeToSurface(const std::string& text, int zoom, int kerning) {
{
auto width = lenght(text, kerning) * zoom; auto width = lenght(text, kerning) * zoom;
auto height = box_height_ * zoom; auto height = box_height_ * zoom;
auto surface = std::make_shared<Surface>(width, height); auto surface = std::make_shared<Surface>(width, height);
@@ -154,8 +144,7 @@ std::shared_ptr<Surface> Text::writeToSurface(const std::string &text, int zoom,
} }
// Escribe el texto con extras en una surface // Escribe el texto con extras en una surface
std::shared_ptr<Surface> Text::writeDXToSurface(Uint8 flags, const std::string &text, int kerning, Uint8 textColor, Uint8 shadow_distance, Uint8 shadow_color, int lenght) std::shared_ptr<Surface> Text::writeDXToSurface(Uint8 flags, const std::string& text, int kerning, Uint8 textColor, Uint8 shadow_distance, Uint8 shadow_color, int lenght) {
{
auto width = Text::lenght(text, kerning) + shadow_distance; auto width = Text::lenght(text, kerning) + shadow_distance;
auto height = box_height_ + shadow_distance; auto height = box_height_ + shadow_distance;
auto surface = std::make_shared<Surface>(width, height); auto surface = std::make_shared<Surface>(width, height);
@@ -169,18 +158,15 @@ std::shared_ptr<Surface> Text::writeDXToSurface(Uint8 flags, const std::string &
} }
// Escribe el texto con colores // Escribe el texto con colores
void Text::writeColored(int x, int y, const std::string &text, Uint8 color, int kerning, int lenght) void Text::writeColored(int x, int y, const std::string& text, Uint8 color, int kerning, int lenght) {
{
int shift = 0; int shift = 0;
if (lenght == -1) if (lenght == -1) {
{
lenght = text.length(); lenght = text.length();
} }
sprite_->setY(y); sprite_->setY(y);
for (int i = 0; i < lenght; ++i) for (int i = 0; i < lenght; ++i) {
{
auto index = static_cast<int>(text[i]); auto index = static_cast<int>(text[i]);
sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_); sprite_->setClip(offset_[index].x, offset_[index].y, box_width_, box_height_);
sprite_->setX(x + shift); sprite_->setX(x + shift);
@@ -190,65 +176,52 @@ void Text::writeColored(int x, int y, const std::string &text, Uint8 color, int
} }
// Escribe el texto con sombra // Escribe el texto con sombra
void Text::writeShadowed(int x, int y, const std::string &text, Uint8 color, Uint8 shadow_distance, int kerning, int lenght) void Text::writeShadowed(int x, int y, const std::string& text, Uint8 color, Uint8 shadow_distance, int kerning, int lenght) {
{
writeColored(x + shadow_distance, y + shadow_distance, text, color, kerning, lenght); writeColored(x + shadow_distance, y + shadow_distance, text, color, kerning, lenght);
write(x, y, text, kerning, lenght); write(x, y, text, kerning, lenght);
} }
// Escribe el texto centrado en un punto x // Escribe el texto centrado en un punto x
void Text::writeCentered(int x, int y, const std::string &text, int kerning, int lenght) void Text::writeCentered(int x, int y, const std::string& text, int kerning, int lenght) {
{
x -= (Text::lenght(text, kerning) / 2); x -= (Text::lenght(text, kerning) / 2);
write(x, y, text, kerning, lenght); write(x, y, text, kerning, lenght);
} }
// Escribe texto con extras // Escribe texto con extras
void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning, Uint8 textColor, Uint8 shadow_distance, Uint8 shadow_color, int lenght) void Text::writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning, Uint8 textColor, Uint8 shadow_distance, Uint8 shadow_color, int lenght) {
{
const auto centered = ((flags & TEXT_CENTER) == TEXT_CENTER); const auto centered = ((flags & TEXT_CENTER) == TEXT_CENTER);
const auto shadowed = ((flags & TEXT_SHADOW) == TEXT_SHADOW); const auto shadowed = ((flags & TEXT_SHADOW) == TEXT_SHADOW);
const auto colored = ((flags & TEXT_COLOR) == TEXT_COLOR); const auto colored = ((flags & TEXT_COLOR) == TEXT_COLOR);
const auto stroked = ((flags & TEXT_STROKE) == TEXT_STROKE); const auto stroked = ((flags & TEXT_STROKE) == TEXT_STROKE);
if (centered) if (centered) {
{
x -= (Text::lenght(text, kerning) / 2); x -= (Text::lenght(text, kerning) / 2);
} }
if (shadowed) if (shadowed) {
{
writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, lenght); writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, lenght);
} }
if (stroked) if (stroked) {
{ for (int dist = 1; dist <= shadow_distance; ++dist) {
for (int dist = 1; dist <= shadow_distance; ++dist) for (int dy = -dist; dy <= dist; ++dy) {
{ for (int dx = -dist; dx <= dist; ++dx) {
for (int dy = -dist; dy <= dist; ++dy)
{
for (int dx = -dist; dx <= dist; ++dx)
{
writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght); writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght);
} }
} }
} }
} }
if (colored) if (colored) {
{
writeColored(x, y, text, textColor, kerning, lenght); writeColored(x, y, text, textColor, kerning, lenght);
} } else {
else
{
writeColored(x, y, text, textColor, kerning, lenght); writeColored(x, y, text, textColor, kerning, lenght);
// write(x, y, text, kerning, lenght); // write(x, y, text, kerning, lenght);
} }
} }
// Obtiene la longitud en pixels de una cadena // Obtiene la longitud en pixels de una cadena
int Text::lenght(const std::string &text, int kerning) const int Text::lenght(const std::string& text, int kerning) const {
{
int shift = 0; int shift = 0;
for (size_t i = 0; i < text.length(); ++i) for (size_t i = 0; i < text.length(); ++i)
shift += (offset_[static_cast<int>(text[i])].w + kerning); shift += (offset_[static_cast<int>(text[i])].w + kerning);
@@ -258,13 +231,11 @@ int Text::lenght(const std::string &text, int kerning) const
} }
// Devuelve el valor de la variable // Devuelve el valor de la variable
int Text::getCharacterSize() const int Text::getCharacterSize() const {
{
return box_width_; return box_width_;
} }
// Establece si se usa un tamaño fijo de letra // Establece si se usa un tamaño fijo de letra
void Text::setFixedWidth(bool value) void Text::setFixedWidth(bool value) {
{
fixed_width_ = value; fixed_width_ = value;
} }

View File

@@ -1,8 +1,10 @@
#pragma once #pragma once
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint8
#include <memory> // Para shared_ptr, unique_ptr #include <memory> // Para shared_ptr, unique_ptr
#include <string> // Para string #include <string> // Para string
#include "s_sprite.h" // Para SSprite #include "s_sprite.h" // Para SSprite
class Surface; // lines 8-8 class Surface; // lines 8-8
@@ -11,25 +13,22 @@ constexpr int TEXT_SHADOW = 2;
constexpr int TEXT_CENTER = 4; constexpr int TEXT_CENTER = 4;
constexpr int TEXT_STROKE = 8; constexpr int TEXT_STROKE = 8;
struct TextOffset struct TextOffset {
{
int x, y, w; int x, y, w;
}; };
struct TextFile struct TextFile {
{
int box_width; // Anchura de la caja de cada caracter en el png 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 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 TextOffset offset[128]; // Vector con las posiciones y ancho de cada letra
}; };
// Llena una estructuta TextFile desde un fichero // Llena una estructuta TextFile desde un fichero
std::shared_ptr<TextFile> loadTextFile(const std::string &file_path); std::shared_ptr<TextFile> loadTextFile(const std::string& file_path);
// Clase texto. Pinta texto en pantalla a partir de un bitmap // Clase texto. Pinta texto en pantalla a partir de un bitmap
class Text class Text {
{ private:
private:
// Objetos y punteros // Objetos y punteros
std::unique_ptr<SSprite> sprite_ = nullptr; // Objeto con los graficos para el texto std::unique_ptr<SSprite> sprite_ = nullptr; // Objeto con los graficos para el texto
@@ -39,37 +38,37 @@ private:
bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija en todas las letras 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 TextOffset offset_[128] = {}; // Vector con las posiciones y ancho de cada letra
public: public:
// Constructor // Constructor
Text(std::shared_ptr<Surface> surface, const std::string &text_file); Text(std::shared_ptr<Surface> surface, const std::string& text_file);
Text(std::shared_ptr<Surface> surface, std::shared_ptr<TextFile> text_file); Text(std::shared_ptr<Surface> surface, std::shared_ptr<TextFile> text_file);
// Destructor // Destructor
~Text() = default; ~Text() = default;
// Escribe el texto en pantalla // Escribe el texto en pantalla
void write(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); void write(int x, int y, const std::string& text, int kerning = 1, int lenght = -1);
// Escribe el texto en una textura // Escribe el texto en una textura
std::shared_ptr<Surface> writeToSurface(const std::string &text, int zoom = 1, int kerning = 1); std::shared_ptr<Surface> writeToSurface(const std::string& text, int zoom = 1, int kerning = 1);
// Escribe el texto con extras en una textura // Escribe el texto con extras en una textura
std::shared_ptr<Surface> writeDXToSurface(Uint8 flags, const std::string &text, int kerning = 1, Uint8 textColor = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1); std::shared_ptr<Surface> writeDXToSurface(Uint8 flags, const std::string& text, int kerning = 1, Uint8 textColor = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1);
// Escribe el texto con colores // Escribe el texto con colores
void writeColored(int x, int y, const std::string &text, Uint8 color, int kerning = 1, int lenght = -1); void writeColored(int x, int y, const std::string& text, Uint8 color, int kerning = 1, int lenght = -1);
// Escribe el texto con sombra // Escribe el texto con sombra
void writeShadowed(int x, int y, const std::string &text, Uint8 color, Uint8 shadow_distance = 1, int kerning = 1, int lenght = -1); void writeShadowed(int x, int y, const std::string& text, Uint8 color, Uint8 shadow_distance = 1, int kerning = 1, int lenght = -1);
// Escribe el texto centrado en un punto x // Escribe el texto centrado en un punto x
void writeCentered(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); void writeCentered(int x, int y, const std::string& text, int kerning = 1, int lenght = -1);
// Escribe texto con extras // Escribe texto con extras
void writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning = 1, Uint8 textColor = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1); void writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning = 1, Uint8 textColor = Uint8(), Uint8 shadow_distance = 1, Uint8 shadow_color = Uint8(), int lenght = -1);
// Obtiene la longitud en pixels de una cadena // Obtiene la longitud en pixels de una cadena
int lenght(const std::string &text, int kerning = 1) const; int lenght(const std::string& text, int kerning = 1) const;
// Devuelve el valor de la variable // Devuelve el valor de la variable
int getCharacterSize() const; int getCharacterSize() const;

View File

@@ -1,60 +1,54 @@
#include "texture.h" #include "texture.h"
#include <SDL2/SDL_error.h> // Para SDL_GetError
#include <SDL2/SDL_surface.h> // Para SDL_CreateRGBSurfaceWithFormatFrom #include <SDL3/SDL_error.h> // Para SDL_GetError
#include <SDL3/SDL_surface.h> // Para SDL_CreateRGBSurfaceWithFormatFrom
#include <iostream> // Para basic_ostream, operator<<, endl, cout #include <iostream> // Para basic_ostream, operator<<, endl, cout
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <string> // Para char_traits, operator<<, string, opera... #include <string> // Para char_traits, operator<<, string, opera...
#include <vector> // Para vector #include <vector> // Para vector
#include "utils.h" // Para getFileName, Color, printWithDots #include "utils.h" // Para getFileName, Color, printWithDots
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h" // para stbi_failure_reason, stbi_image_free #include "stb_image.h" // para stbi_failure_reason, stbi_image_free
// Constructor // Constructor
Texture::Texture(SDL_Renderer *renderer, const std::string &path) Texture::Texture(SDL_Renderer* renderer, const std::string& path)
: renderer_(renderer), : renderer_(renderer),
path_(path) path_(path) {
{
// Carga el fichero en la textura // Carga el fichero en la textura
if (!path_.empty()) if (!path_.empty()) {
{
// Obtiene la extensión // Obtiene la extensión
const std::string extension = path_.substr(path_.find_last_of(".") + 1); const std::string extension = path_.substr(path_.find_last_of(".") + 1);
// .png // .png
if (extension == "png") if (extension == "png") {
{
loadFromFile(path_); loadFromFile(path_);
} }
} }
} }
// Destructor // Destructor
Texture::~Texture() Texture::~Texture() {
{
unloadTexture(); unloadTexture();
palettes_.clear(); palettes_.clear();
} }
// Carga una imagen desde un fichero // Carga una imagen desde un fichero
bool Texture::loadFromFile(const std::string &file_path) bool Texture::loadFromFile(const std::string& file_path) {
{ if (file_path.empty()) {
if (file_path.empty())
{
return false; return false;
} }
int req_format = STBI_rgb_alpha; int req_format = STBI_rgb_alpha;
int width, height, orig_format; int width, height, orig_format;
unsigned char *data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format); unsigned char* data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format);
if (!data) if (!data) {
{
std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl; std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl;
throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path)); throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path));
} } else {
else
{
printWithDots("Image : ", getFileName(file_path), "[ LOADED ]"); printWithDots("Image : ", getFileName(file_path), "[ LOADED ]");
} }
@@ -69,24 +63,18 @@ bool Texture::loadFromFile(const std::string &file_path)
unloadTexture(); unloadTexture();
// La textura final // La textura final
SDL_Texture *newTexture = nullptr; SDL_Texture* newTexture = nullptr;
// Carga la imagen desde una ruta específica // Carga la imagen desde una ruta específica
auto loadedSurface = SDL_CreateRGBSurfaceWithFormatFrom(static_cast<void *>(data), width, height, depth, pitch, pixel_format); auto loadedSurface = SDL_CreateRGBSurfaceWithFormatFrom(static_cast<void*>(data), width, height, depth, pitch, pixel_format);
if (loadedSurface == nullptr) if (loadedSurface == nullptr) {
{
std::cout << "Unable to load image " << file_path << std::endl; std::cout << "Unable to load image " << file_path << std::endl;
} } else {
else
{
// Crea la textura desde los pixels de la surface // Crea la textura desde los pixels de la surface
newTexture = SDL_CreateTextureFromSurface(renderer_, loadedSurface); newTexture = SDL_CreateTextureFromSurface(renderer_, loadedSurface);
if (newTexture == nullptr) if (newTexture == nullptr) {
{
std::cout << "Unable to create texture from " << file_path << "! SDL Error: " << SDL_GetError() << std::endl; std::cout << "Unable to create texture from " << file_path << "! SDL Error: " << SDL_GetError() << std::endl;
} } else {
else
{
// Obtiene las dimensiones de la imagen // Obtiene las dimensiones de la imagen
width_ = loadedSurface->w; width_ = loadedSurface->w;
height_ = loadedSurface->h; height_ = loadedSurface->h;
@@ -103,16 +91,12 @@ bool Texture::loadFromFile(const std::string &file_path)
} }
// Crea una textura en blanco // Crea una textura en blanco
bool Texture::createBlank(int width, int height, SDL_PixelFormatEnum format, SDL_TextureAccess access) bool Texture::createBlank(int width, int height, SDL_PixelFormatEnum format, SDL_TextureAccess access) {
{
// Crea una textura sin inicializar // Crea una textura sin inicializar
texture_ = SDL_CreateTexture(renderer_, format, access, width, height); texture_ = SDL_CreateTexture(renderer_, format, access, width, height);
if (!texture_) if (!texture_) {
{
std::cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << std::endl; std::cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << std::endl;
} } else {
else
{
width_ = width; width_ = width;
height_ = height; height_ = height;
} }
@@ -121,11 +105,9 @@ bool Texture::createBlank(int width, int height, SDL_PixelFormatEnum format, SDL
} }
// Libera la memoria de la textura // Libera la memoria de la textura
void Texture::unloadTexture() void Texture::unloadTexture() {
{
// Libera la textura // Libera la textura
if (texture_) if (texture_) {
{
SDL_DestroyTexture(texture_); SDL_DestroyTexture(texture_);
texture_ = nullptr; texture_ = nullptr;
width_ = 0; width_ = 0;
@@ -144,21 +126,18 @@ void Texture::setBlendMode(SDL_BlendMode blending) { SDL_SetTextureBlendMode(tex
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 // Renderiza la textura en un punto específico
void Texture::render(int x, int y, SDL_Rect *clip, float zoomW, float zoomH, double angle, SDL_Point *center, SDL_RendererFlip flip) void Texture::render(int x, int y, SDL_Rect* clip, float zoomW, float zoomH, double angle, SDL_Point* center, SDL_RendererFlip flip) {
{
// Establece el destino de renderizado en la pantalla // Establece el destino de renderizado en la pantalla
SDL_Rect renderQuad = {x, y, width_, height_}; SDL_Rect renderQuad = {x, y, width_, height_};
// Obtiene las dimesiones del clip de renderizado // Obtiene las dimesiones del clip de renderizado
if (clip != nullptr) if (clip != nullptr) {
{
renderQuad.w = clip->w; renderQuad.w = clip->w;
renderQuad.h = clip->h; renderQuad.h = clip->h;
} }
// Calcula el zoom y las coordenadas // Calcula el zoom y las coordenadas
if (zoomH != 1.0f || zoomW != 1.0f) if (zoomH != 1.0f || zoomW != 1.0f) {
{
renderQuad.x = renderQuad.x + (renderQuad.w / 2); renderQuad.x = renderQuad.x + (renderQuad.w / 2);
renderQuad.y = renderQuad.y + (renderQuad.h / 2); renderQuad.y = renderQuad.y + (renderQuad.h / 2);
renderQuad.w = renderQuad.w * zoomW; renderQuad.w = renderQuad.w * zoomW;
@@ -172,7 +151,7 @@ void Texture::render(int x, int y, SDL_Rect *clip, float zoomW, float zoomH, dou
} }
// Establece la textura como objetivo de renderizado // 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 // Obtiene el ancho de la imagen
int Texture::getWidth() { return width_; } int Texture::getWidth() { return width_; }
@@ -184,7 +163,7 @@ int Texture::getHeight() { return height_; }
bool Texture::reLoad() { return loadFromFile(path_); } bool Texture::reLoad() { return loadFromFile(path_); }
// Obtiene la textura // Obtiene la textura
SDL_Texture *Texture::getSDLTexture() { return texture_; } SDL_Texture* Texture::getSDLTexture() { return texture_; }
// Obtiene el renderizador // Obtiene el renderizador
SDL_Renderer *Texture::getRenderer() { return renderer_; } SDL_Renderer* Texture::getRenderer() { return renderer_; }

View File

@@ -1,20 +1,20 @@
#pragma once #pragma once
#include <SDL2/SDL_blendmode.h> // Para SDL_BlendMode #include <SDL3/SDL_blendmode.h> // Para SDL_BlendMode
#include <SDL2/SDL_pixels.h> // Para SDL_PIXELFORMAT_RGBA8888, SDL_PixelF... #include <SDL3/SDL_pixels.h> // Para SDL_PIXELFORMAT_RGBA8888, SDL_PixelF...
#include <SDL2/SDL_rect.h> // Para SDL_Point, SDL_Rect #include <SDL3/SDL_rect.h> // Para SDL_Point, SDL_Rect
#include <SDL2/SDL_render.h> // Para SDL_Renderer, SDL_FLIP_NONE, SDL_TEX... #include <SDL3/SDL_render.h> // Para SDL_Renderer, SDL_FLIP_NONE, SDL_TEX...
#include <SDL2/SDL_stdinc.h> // Para Uint8, Uint32 #include <SDL3/SDL_stdinc.h> // Para Uint8, Uint32
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
struct Color; // lines 11-11 struct Color; // lines 11-11
class Texture class Texture {
{ private:
private:
// Objetos y punteros // Objetos y punteros
SDL_Renderer *renderer_; // Renderizador donde dibujar la textura SDL_Renderer* renderer_; // Renderizador donde dibujar la textura
SDL_Texture *texture_ = nullptr; // La textura SDL_Texture* texture_ = nullptr; // La textura
// Variables // Variables
std::string path_; // Ruta de la imagen de la textura std::string path_; // Ruta de la imagen de la textura
@@ -25,15 +25,15 @@ private:
// Libera la memoria de la textura // Libera la memoria de la textura
void unloadTexture(); void unloadTexture();
public: public:
// Constructor // Constructor
explicit Texture(SDL_Renderer *renderer, const std::string &path = std::string()); explicit Texture(SDL_Renderer* renderer, const std::string& path = std::string());
// Destructor // Destructor
~Texture(); ~Texture();
// Carga una imagen desde un fichero // Carga una imagen desde un fichero
bool loadFromFile(const std::string &path); bool loadFromFile(const std::string& path);
// Crea una textura en blanco // Crea una textura en blanco
bool createBlank(int width, int height, SDL_PixelFormatEnum format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess = SDL_TEXTUREACCESS_TARGET); bool createBlank(int width, int height, SDL_PixelFormatEnum format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess = SDL_TEXTUREACCESS_TARGET);
@@ -49,10 +49,10 @@ public:
void setAlpha(Uint8 alpha); void setAlpha(Uint8 alpha);
// Renderiza la textura en un punto específico // Renderiza la textura en un punto específico
void render(int x, int y, SDL_Rect *clip = nullptr, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_Point *center = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE); void render(int x, int y, SDL_Rect* clip = nullptr, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_Point* center = nullptr, SDL_RendererFlip flip = SDL_FLIP_NONE);
// Establece la textura como objetivo de renderizado // Establece la textura como objetivo de renderizado
void setAsRenderTarget(SDL_Renderer *renderer); void setAsRenderTarget(SDL_Renderer* renderer);
// Obtiene el ancho de la imagen // Obtiene el ancho de la imagen
int getWidth(); int getWidth();
@@ -64,8 +64,8 @@ public:
bool reLoad(); bool reLoad();
// Obtiene la textura // Obtiene la textura
SDL_Texture *getSDLTexture(); SDL_Texture* getSDLTexture();
// Obtiene el renderizador // Obtiene el renderizador
SDL_Renderer *getRenderer(); SDL_Renderer* getRenderer();
}; };

View File

@@ -1,377 +0,0 @@
#include "title.h"
#include <SDL2/SDL_events.h> // Para SDL_PollEvent, SDL_Event, SDL_KEYDOWN
#include <SDL2/SDL_scancode.h> // Para SDL_SCANCODE_1, SDL_SCANCODE_2
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para clamp
#include "cheevos.h" // Para Cheevos, Achievement
#include "defines.h" // Para PLAY_AREA_CENTER_X, GAMECANVAS_WIDTH
#include "global_events.h" // Para check
#include "global_inputs.h" // Para check
#include "input.h" // Para Input, InputAction, INPUT_DO_NOT_ALLOW_REPEAT, REP...
#include "options.h" // Para Options, options, SectionState, Section
#include "resource.h" // Para Resource
#include "s_sprite.h" // Para SSprite
#include "screen.h" // Para Screen
#include "surface.h" // Para Surface
#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR
#include "utils.h" // Para stringToColor, PaletteColor, playMusic
// Constructor
Title::Title()
: title_logo_surface_(Resource::get()->getSurface("title_logo.gif")),
title_logo_sprite_(std::make_shared<SSprite>(title_logo_surface_, 29, 9, title_logo_surface_->getWidth(), title_logo_surface_->getHeight())),
loading_screen_surface_(Resource::get()->getSurface("loading_screen_color.gif")),
loading_screen_sprite_(std::make_shared<SSprite>(loading_screen_surface_, 0, 0, loading_screen_surface_->getWidth(), loading_screen_surface_->getHeight())),
bg_surface_(std::make_shared<Surface>(options.game.width, options.game.height))
{
// Inicializa variables
state_ = options.section.subsection == Subsection::TITLE_WITH_LOADING_SCREEN ? TitleState::SHOW_LOADING_SCREEN : TitleState::SHOW_MENU;
options.section.section = Section::TITLE;
options.section.subsection = Subsection::NONE;
initMarquee();
// Crea y rellena la textura para mostrar los logros
createCheevosTexture();
// Cambia el color del borde
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
// Rellena la textura de fondo con todos los gráficos
fillSurface();
// Inicia la musica
playMusic("title.ogg");
}
// Inicializa la marquesina
void Title::initMarquee()
{
letters_.clear();
long_text_ = "HEY JAILERS!! IT'S 2022 AND WE'RE STILL ROCKING LIKE IT'S 1998!!! HAVE YOU HEARD IT? JAILGAMES ARE BACK!! YEEESSS BACK!! MORE THAN 10 TITLES ON JAILDOC'S KITCHEN!! THATS A LOOOOOOT OF JAILGAMES, BUT WHICH ONE WILL STRIKE FIRST? THERE IS ALSO A NEW DEVICE TO COME THAT WILL BLOW YOUR MIND WITH JAILGAMES ON THE GO: P.A.C.O. BUT WAIT! WHAT'S THAT BEAUTY I'M SEEING RIGHT OVER THERE?? OOOH THAT TINY MINIASCII IS PURE LOVE!! I WANT TO LICK EVERY BYTE OF IT!! OH SHIT! AND DON'T FORGET TO BRING BACK THOSE OLD AND FAT MS-DOS JAILGAMES TO GITHUB TO KEEP THEM ALIVE!! WHAT WILL BE THE NEXT JAILDOC RELEASE? WHAT WILL BE THE NEXT PROJECT TO COME ALIVE?? OH BABY WE DON'T KNOW BUT HERE YOU CAN FIND THE ANSWER, YOU JUST HAVE TO COMPLETE JAILDOCTOR'S DILEMMA ... COULD YOU?";
for (int i = 0; i < (int)long_text_.length(); ++i)
{
TitleLetter l;
l.letter = long_text_.substr(i, 1);
l.x = 256;
l.enabled = false;
letters_.push_back(l);
}
letters_[0].enabled = true;
}
// Comprueba el manejador de eventos
void Title::checkEvents()
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
globalEvents::check(event);
// Solo se comprueban estas teclas si no está activo el menu de logros
if (event.type == SDL_KEYDOWN)
{
if (!show_cheevos_)
{
switch (event.key.keysym.scancode)
{
case SDL_SCANCODE_1:
options.section.section = Section::GAME;
options.section.subsection = Subsection::NONE;
break;
case SDL_SCANCODE_2:
show_cheevos_ = true;
break;
default:
break;
}
}
}
}
}
// Comprueba las entradas
void Title::checkInput()
{
if (show_cheevos_)
{
if (Input::get()->checkInput(InputAction::DOWN, INPUT_ALLOW_REPEAT))
{
moveCheevosList(1);
}
else if (Input::get()->checkInput(InputAction::UP, INPUT_ALLOW_REPEAT))
{
moveCheevosList(0);
}
else if (Input::get()->checkInput(InputAction::ACCEPT, INPUT_DO_NOT_ALLOW_REPEAT))
{
hideCheevosList();
counter_ = 0;
}
}
if (Input::get()->checkInput(InputAction::ACCEPT, INPUT_DO_NOT_ALLOW_REPEAT))
{
if (state_ == TitleState::SHOW_LOADING_SCREEN)
{
state_ = TitleState::FADE_LOADING_SCREEN;
}
}
globalInputs::check();
}
// Actualiza la marquesina
void Title::updateMarquee()
{
const auto TEXT = Resource::get()->getText("gauntlet");
for (int i = 0; i < (int)letters_.size(); ++i)
{
if (letters_[i].enabled)
{
letters_[i].x -= marquee_speed_;
if (letters_[i].x < -10)
{
letters_[i].enabled = false;
}
}
else
{
if (i > 0 && letters_[i - 1].x < 256 && letters_[i - 1].enabled)
{
letters_[i].enabled = true;
letters_[i].x = letters_[i - 1].x + TEXT->lenght(letters_[i - 1].letter) + 1;
}
}
}
// Comprueba si ha terminado la marquesina y la reinicia
if (letters_[letters_.size() - 1].x < -10)
{
// Inicializa la marquesina
initMarquee();
}
}
// Dibuja la marquesina
void Title::renderMarquee()
{
const auto TEXT = Resource::get()->getText("gauntlet");
for (const auto &l : letters_)
{
if (l.enabled)
{
TEXT->writeColored(l.x, 184, l.letter, static_cast<Uint8>(PaletteColor::BRIGHT_RED));
}
}
}
// Actualiza las variables
void Title::update()
{
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
if (SDL_GetTicks() - ticks_ > GAME_SPEED)
{
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Comprueba las entradas
checkInput();
Screen::get()->update();
// Incrementa el contador
counter_++;
switch (state_)
{
case TitleState::SHOW_LOADING_SCREEN:
if (counter_ == 500)
{
counter_ = 0;
state_ = TitleState::FADE_LOADING_SCREEN;
}
break;
case TitleState::FADE_LOADING_SCREEN:
if (counter_ % 4 == 0)
{
if (loading_screen_surface_->fadeSubPalette())
{
counter_ = 0;
state_ = TitleState::SHOW_MENU;
}
}
break;
case TitleState::SHOW_MENU:
// Actualiza la marquesina
updateMarquee();
// Si el contador alcanza cierto valor, termina la seccion
if (counter_ == 2200)
{
if (!show_cheevos_)
{
options.section.section = Section::CREDITS;
options.section.subsection = Subsection::NONE;
}
}
break;
default:
break;
}
}
}
// Dibuja en pantalla
void Title::render()
{
// Prepara para empezar a dibujar en la textura de juego
Screen::get()->start();
Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));
switch (state_)
{
case TitleState::SHOW_MENU:
// Dibuja la textura de fondo
bg_surface_->render(0, 0);
// Dibuja la marquesina
renderMarquee();
// Dibuja la información de logros
if (show_cheevos_)
{
cheevos_sprite_->render();
}
break;
case TitleState::SHOW_LOADING_SCREEN:
case TitleState::FADE_LOADING_SCREEN:
loading_screen_sprite_->render();
title_logo_sprite_->render();
break;
default:
break;
}
// Vuelca el contenido del renderizador en pantalla
Screen::get()->render();
}
// Bucle para el logo del juego
void Title::run()
{
while (options.section.section == Section::TITLE)
{
update();
checkEvents();
render();
}
}
// Desplaza la lista de logros
void Title::moveCheevosList(int direction)
{
// Modifica la posición de la ventana de vista
constexpr int SPEED = 2;
cheevos_surface_view_.y = direction == 0 ? cheevos_surface_view_.y - SPEED : cheevos_surface_view_.y + SPEED;
// Ajusta los limites
const int BOTTOM = cheevos_surface_->getHeight() - cheevos_surface_view_.h;
cheevos_surface_view_.y = std::clamp(cheevos_surface_view_.y, 0, BOTTOM);
cheevos_sprite_->setClip(cheevos_surface_view_);
}
// Rellena la textura de fondo con todos los gráficos
void Title::fillSurface()
{
// Coloca el puntero del renderizador sobre la textura
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(bg_surface_);
// Rellena la textura de color
bg_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));
// Pinta el gráfico del titulo a partir del sprite
title_logo_sprite_->render();
// Escribe el texto en la textura
auto text = Resource::get()->getText("smb2");
const Uint8 COLOR = stringToColor("green");
const int TEXT_SIZE = text->getCharacterSize();
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 11 * TEXT_SIZE, "1.PLAY", 1, COLOR);
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 13 * TEXT_SIZE, "2.ACHIEVEMENTS", 1, COLOR);
text->writeDX(TEXT_CENTER | TEXT_COLOR, PLAY_AREA_CENTER_X, 15 * TEXT_SIZE, "3.REDEFINE KEYS", 1, COLOR);
// Devuelve el puntero del renderizador a su sitio
Screen::get()->setRendererSurface(previuos_renderer);
}
// Crea y rellena la textura para mostrar los logros
void Title::createCheevosTexture()
{
// Crea la textura con el listado de logros
const auto CHEEVOS_LIST = Cheevos::get()->list();
const auto TEXT = Resource::get()->getText("subatomic");
constexpr int CHEEVOS_TEXTURE_WIDTH = 200;
constexpr int CHEEVOS_TEXTURE_VIEW_HEIGHT = 110 - 8;
constexpr int CHEEVOS_TEXTURE_POS_Y = 73;
constexpr int CHEEVOS_PADDING = 10;
const int CHEEVO_HEIGHT = CHEEVOS_PADDING + (TEXT->getCharacterSize() * 2) + 1;
const int CHEEVOS_TEXTURE_HEIGHT = (CHEEVO_HEIGHT * CHEEVOS_LIST.size()) + 2 + TEXT->getCharacterSize() + 8;
cheevos_surface_ = std::make_shared<Surface>(CHEEVOS_TEXTURE_WIDTH, CHEEVOS_TEXTURE_HEIGHT);
// Prepara para dibujar sobre la textura
auto previuos_renderer = Screen::get()->getRendererSurface();
Screen::get()->setRendererSurface(cheevos_surface_);
// Rellena la textura con color sólido
const Uint8 CHEEVOS_BG_COLOR = static_cast<Uint8>(PaletteColor::BLACK);
cheevos_surface_->clear(CHEEVOS_BG_COLOR);
// Escribe la lista de logros en la textura
const std::string CHEEVOS_OWNER = "ACHIEVEMENTS";
const std::string CHEEVOS_LIST_CAPTION = CHEEVOS_OWNER + " (" + std::to_string(Cheevos::get()->getTotalUnlockedAchievements()) + " / " + std::to_string(Cheevos::get()->size()) + ")";
int pos = 2;
TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, cheevos_surface_->getWidth() / 2, pos, CHEEVOS_LIST_CAPTION, 1, stringToColor("bright_green"));
pos += TEXT->getCharacterSize();
const Uint8 CHEEVO_LOCKED_COLOR = stringToColor("white");
const Uint8 CHEEVO_UNLOCKED_COLOR = stringToColor("bright_green");
constexpr int LINE_X1 = (CHEEVOS_TEXTURE_WIDTH / 7) * 3;
constexpr int LINE_X2 = LINE_X1 + ((CHEEVOS_TEXTURE_WIDTH / 7) * 1);
for (const auto &cheevo : CHEEVOS_LIST)
{
const Uint8 CHEEVO_COLOR = cheevo.completed ? CHEEVO_UNLOCKED_COLOR : CHEEVO_LOCKED_COLOR;
pos += CHEEVOS_PADDING;
constexpr int HALF = CHEEVOS_PADDING / 2;
cheevos_surface_->drawLine(LINE_X1, pos - HALF - 1, LINE_X2, pos - HALF - 1, CHEEVO_COLOR);
TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.caption, 1, CHEEVO_COLOR);
pos += TEXT->getCharacterSize() + 1;
TEXT->writeDX(TEXT_CENTER | TEXT_COLOR, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.description, 1, CHEEVO_COLOR);
pos += TEXT->getCharacterSize();
}
// Restablece el RenderSurface
Screen::get()->setRendererSurface(previuos_renderer);
// Crea el sprite para el listado de logros
cheevos_sprite_ = std::make_shared<SSprite>(cheevos_surface_, (GAMECANVAS_WIDTH - cheevos_surface_->getWidth()) / 2, CHEEVOS_TEXTURE_POS_Y, cheevos_surface_->getWidth(), cheevos_surface_->getHeight());
cheevos_surface_view_ = {0, 0, cheevos_surface_->getWidth(), CHEEVOS_TEXTURE_VIEW_HEIGHT};
cheevos_sprite_->setClip(cheevos_surface_view_);
}
// Oculta la lista de logros
void Title::hideCheevosList()
{
show_cheevos_ = false;
cheevos_surface_view_.y = 0;
cheevos_sprite_->setClip(cheevos_surface_view_);
}

View File

@@ -1,89 +0,0 @@
#pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect
#include <SDL2/SDL_stdinc.h> // Para Uint32
#include <memory> // Para shared_ptr
#include <string> // Para string
#include <vector> // Para vector
class SSprite; // lines 9-9
class Surface; // lines 10-10
class Title
{
private:
struct TitleLetter
{
std::string letter; // Letra a escribir
int x; // Posición en el eje x
bool enabled; // Solo se escriben y mueven si estan habilitadas
};
enum class TitleState
{
SHOW_LOADING_SCREEN,
FADE_LOADING_SCREEN,
SHOW_MENU
};
// Objetos y punteros
std::shared_ptr<Surface> title_logo_surface_; // Textura con los graficos
std::shared_ptr<SSprite> title_logo_sprite_; // SSprite para manejar la surface
std::shared_ptr<Surface> loading_screen_surface_; // Surface con los gráficos de la pantalla de carga
std::shared_ptr<SSprite> loading_screen_sprite_; // SSprite con los gráficos de la pantalla de carga
std::shared_ptr<Surface> bg_surface_; // Textura para dibujar el fondo de la pantalla
std::shared_ptr<Surface> cheevos_surface_; // Textura con la lista de logros
std::shared_ptr<SSprite> cheevos_sprite_; // SSprite para manejar la surface con la lista de logros
// Variables
int counter_ = 0; // Contador
std::string long_text_; // Texto que aparece en la parte inferior del titulo
Uint32 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa
std::vector<TitleLetter> letters_; // Vector con las letras de la marquesina
int marquee_speed_ = 2; // Velocidad de desplazamiento de la marquesina
bool show_cheevos_ = false; // Indica si se muestra por pantalla el listado de logros
SDL_Rect cheevos_surface_view_; // Zona visible de la surface con el listado de logros
TitleState state_; // Estado en el que se encuentra el bucle principal
// Actualiza las variables
void update();
// Dibuja en pantalla
void render();
// Comprueba el manejador de eventos
void checkEvents();
// Comprueba las entradas
void checkInput();
// Inicializa la marquesina
void initMarquee();
// Actualiza la marquesina
void updateMarquee();
// Dibuja la marquesina
void renderMarquee();
// Desplaza la lista de logros
void moveCheevosList(int direction);
// Rellena la surface de fondo con todos los gráficos
void fillSurface();
// Crea y rellena la surface para mostrar los logros
void createCheevosTexture();
// Oculta la lista de logros
void hideCheevosList();
public:
// Constructor
Title();
// Destructor
~Title() = default;
// Bucle principal
void run();
};

View File

@@ -1,9 +1,12 @@
#include "notifier.h" #include "notifier.h"
#include <SDL2/SDL_timer.h> // Para SDL_GetTicks
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
#include <algorithm> // Para remove_if #include <algorithm> // Para remove_if
#include <iterator> // Para prev #include <iterator> // Para prev
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include <vector> // Para vector #include <vector> // Para vector
#include "jail_audio.h" // Para JA_PlaySound #include "jail_audio.h" // Para JA_PlaySound
#include "options.h" // Para Options, options, NotificationPosition #include "options.h" // Para Options, options, NotificationPosition
#include "resource.h" // Para Resource #include "resource.h" // Para Resource
@@ -14,28 +17,25 @@
#include "utils.h" // Para PaletteColor #include "utils.h" // Para PaletteColor
// [SINGLETON] // [SINGLETON]
Notifier *Notifier::notifier_ = nullptr; Notifier* Notifier::notifier_ = nullptr;
// [SINGLETON] Crearemos el objeto con esta función estática // [SINGLETON] Crearemos el objeto con esta función estática
void Notifier::init(const std::string &icon_file, const std::string &text) void Notifier::init(const std::string& icon_file, const std::string& text) {
{
Notifier::notifier_ = new Notifier(icon_file, text); Notifier::notifier_ = new Notifier(icon_file, text);
} }
// [SINGLETON] Destruiremos el objeto con esta función estática // [SINGLETON] Destruiremos el objeto con esta función estática
void Notifier::destroy() void Notifier::destroy() {
{
delete Notifier::notifier_; delete Notifier::notifier_;
} }
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él // [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
Notifier *Notifier::get() Notifier* Notifier::get() {
{
return Notifier::notifier_; return Notifier::notifier_;
} }
// Constructor // Constructor
Notifier::Notifier(const std::string &icon_file, const std::string &text) Notifier::Notifier(const std::string& icon_file, const std::string& text)
: icon_surface_(!icon_file.empty() ? Resource::get()->getSurface(icon_file) : nullptr), : icon_surface_(!icon_file.empty() ? Resource::get()->getSurface(icon_file) : nullptr),
text_(Resource::get()->getText(text)), text_(Resource::get()->getText(text)),
bg_color_(options.notifications.color), bg_color_(options.notifications.color),
@@ -43,60 +43,47 @@ Notifier::Notifier(const std::string &icon_file, const std::string &text)
has_icons_(!icon_file.empty()) {} has_icons_(!icon_file.empty()) {}
// Dibuja las notificaciones por pantalla // Dibuja las notificaciones por pantalla
void Notifier::render() void Notifier::render() {
{ for (auto it = notifications_.rbegin(); it != notifications_.rend(); ++it) {
for (auto it = notifications_.rbegin(); it != notifications_.rend(); ++it)
{
it->sprite->render(); it->sprite->render();
} }
} }
// Actualiza el estado de las notificaiones // Actualiza el estado de las notificaiones
void Notifier::update() void Notifier::update() {
{ for (auto& notification : notifications_) {
for (auto &notification : notifications_)
{
// Si la notificación anterior está "saliendo", no hagas nada // Si la notificación anterior está "saliendo", no hagas nada
if (!notifications_.empty() && &notification != &notifications_.front()) if (!notifications_.empty() && &notification != &notifications_.front()) {
{ const auto& PREVIOUS_NOTIFICATION = *(std::prev(&notification));
const auto &PREVIOUS_NOTIFICATION = *(std::prev(&notification)); if (PREVIOUS_NOTIFICATION.state == NotificationStatus::RISING) {
if (PREVIOUS_NOTIFICATION.state == NotificationStatus::RISING)
{
break; break;
} }
} }
switch (notification.state) switch (notification.state) {
{ case NotificationStatus::RISING: {
case NotificationStatus::RISING:
{
const int DIRECTION = (options.notifications.getVerticalPosition() == NotificationPosition::TOP) ? 1 : -1; const int DIRECTION = (options.notifications.getVerticalPosition() == NotificationPosition::TOP) ? 1 : -1;
notification.rect.y += DIRECTION; notification.rect.y += DIRECTION;
if (notification.rect.y == notification.y) if (notification.rect.y == notification.y) {
{
notification.state = NotificationStatus::STAY; notification.state = NotificationStatus::STAY;
notification.start_time = SDL_GetTicks(); notification.start_time = SDL_GetTicks();
} }
break; break;
} }
case NotificationStatus::STAY: case NotificationStatus::STAY: {
{
notification.elapsed_time = SDL_GetTicks() - notification.start_time; notification.elapsed_time = SDL_GetTicks() - notification.start_time;
if (notification.elapsed_time >= notification.display_duration) if (notification.elapsed_time >= notification.display_duration) {
{
notification.state = NotificationStatus::VANISHING; notification.state = NotificationStatus::VANISHING;
} }
break; break;
} }
case NotificationStatus::VANISHING: case NotificationStatus::VANISHING: {
{
const int DIRECTION = (options.notifications.getVerticalPosition() == NotificationPosition::TOP) ? -1 : 1; const int DIRECTION = (options.notifications.getVerticalPosition() == NotificationPosition::TOP) ? -1 : 1;
notification.rect.y += DIRECTION; notification.rect.y += DIRECTION;
if (notification.rect.y == notification.y - notification.travel_dist) if (notification.rect.y == notification.y - notification.travel_dist) {
{
notification.state = NotificationStatus::FINISHED; notification.state = NotificationStatus::FINISHED;
} }
break; break;
@@ -116,42 +103,33 @@ void Notifier::update()
} }
// Elimina las notificaciones finalizadas // Elimina las notificaciones finalizadas
void Notifier::clearFinishedNotifications() void Notifier::clearFinishedNotifications() {
{
notifications_.erase( notifications_.erase(
std::remove_if(notifications_.begin(), notifications_.end(), std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& notification) {
[](const Notification &notification)
{
return notification.state == NotificationStatus::FINISHED; return notification.state == NotificationStatus::FINISHED;
}), }),
notifications_.end()); notifications_.end());
} }
void Notifier::show(std::vector<std::string> texts, NotificationText text_is, Uint32 display_duration, int icon, bool can_be_removed, const std::string &code) void Notifier::show(std::vector<std::string> texts, NotificationText text_is, Uint32 display_duration, int icon, bool can_be_removed, const std::string& code) {
{
// Si no hay texto, acaba // Si no hay texto, acaba
if (texts.empty()) if (texts.empty()) {
{
return; return;
} }
// Si las notificaciones no se apilan, elimina las anteriores // Si las notificaciones no se apilan, elimina las anteriores
if (!stack_) if (!stack_) {
{
clearNotifications(); clearNotifications();
} }
// Elimina las cadenas vacías // Elimina las cadenas vacías
texts.erase(std::remove_if(texts.begin(), texts.end(), [](const std::string &s) texts.erase(std::remove_if(texts.begin(), texts.end(), [](const std::string& s) { return s.empty(); }),
{ return s.empty(); }),
texts.end()); texts.end());
// Encuentra la cadena más larga // Encuentra la cadena más larga
std::string longest; std::string longest;
for (const auto &text : texts) for (const auto& text : texts) {
{ if (text.length() > longest.length()) {
if (text.length() > longest.length())
{
longest = text; longest = text;
} }
} }
@@ -168,8 +146,7 @@ void Notifier::show(std::vector<std::string> texts, NotificationText text_is, Ui
// Posición horizontal // Posición horizontal
int desp_h = 0; int desp_h = 0;
switch (options.notifications.getHorizontalPosition()) switch (options.notifications.getHorizontalPosition()) {
{
case NotificationPosition::LEFT: case NotificationPosition::LEFT:
desp_h = PADDING_OUT_; desp_h = PADDING_OUT_;
break; break;
@@ -218,8 +195,7 @@ void Notifier::show(std::vector<std::string> texts, NotificationText text_is, Ui
// Dibuja el fondo de la notificación // Dibuja el fondo de la notificación
SDL_Rect rect; SDL_Rect rect;
if (SHAPE == NotificationShape::ROUNDED) if (SHAPE == NotificationShape::ROUNDED) {
{
rect = {4, 0, WIDTH - (4 * 2), HEIGHT}; rect = {4, 0, WIDTH - (4 * 2), HEIGHT};
n.surface->fillRect(&rect, bg_color_); n.surface->fillRect(&rect, bg_color_);
@@ -233,16 +209,14 @@ void Notifier::show(std::vector<std::string> texts, NotificationText text_is, Ui
n.surface->fillRect(&rect, bg_color_); n.surface->fillRect(&rect, bg_color_);
} }
else if (SHAPE == NotificationShape::SQUARED) else if (SHAPE == NotificationShape::SQUARED) {
{
n.surface->clear(bg_color_); n.surface->clear(bg_color_);
SDL_Rect squared_rect = {0, 0, n.surface->getWidth(), n.surface->getHeight()}; SDL_Rect squared_rect = {0, 0, n.surface->getWidth(), n.surface->getHeight()};
n.surface->drawRectBorder(&squared_rect, static_cast<Uint8>(PaletteColor::CYAN)); n.surface->drawRectBorder(&squared_rect, static_cast<Uint8>(PaletteColor::CYAN));
} }
// Dibuja el icono de la notificación // 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<SSprite>(icon_surface_, (SDL_Rect){0, 0, ICON_SIZE_, ICON_SIZE_}); auto sp = std::make_unique<SSprite>(icon_surface_, (SDL_Rect){0, 0, ICON_SIZE_, ICON_SIZE_});
sp->setPosition({PADDING_IN_H, PADDING_IN_V, ICON_SIZE_, ICON_SIZE_}); sp->setPosition({PADDING_IN_H, PADDING_IN_V, ICON_SIZE_, ICON_SIZE_});
sp->setClip({ICON_SIZE_ * (icon % 10), ICON_SIZE_ * (icon / 10), ICON_SIZE_, ICON_SIZE_}); sp->setClip({ICON_SIZE_ * (icon % 10), ICON_SIZE_ * (icon / 10), ICON_SIZE_, ICON_SIZE_});
@@ -252,10 +226,8 @@ void Notifier::show(std::vector<std::string> texts, NotificationText text_is, Ui
// Escribe el texto de la notificación // Escribe el texto de la notificación
const Uint8 COLOR = static_cast<Uint8>(PaletteColor::WHITE); const Uint8 COLOR = static_cast<Uint8>(PaletteColor::WHITE);
int iterator = 0; int iterator = 0;
for (const auto &text : texts) for (const auto& text : texts) {
{ switch (text_is) {
switch (text_is)
{
case NotificationText::LEFT: case NotificationText::LEFT:
text_->writeColored(PADDING_IN_H + ICON_SPACE, PADDING_IN_V + iterator * (text_size + 1), text, COLOR); text_->writeColored(PADDING_IN_H + ICON_SPACE, PADDING_IN_V + iterator * (text_size + 1), text, COLOR);
break; break;
@@ -286,12 +258,9 @@ void Notifier::show(std::vector<std::string> texts, NotificationText text_is, Ui
bool Notifier::isActive() { return !notifications_.empty(); } bool Notifier::isActive() { return !notifications_.empty(); }
// Finaliza y elimnina todas las notificaciones activas // Finaliza y elimnina todas las notificaciones activas
void Notifier::clearNotifications() void Notifier::clearNotifications() {
{ for (auto& notification : notifications_) {
for (auto &notification : notifications_) if (notification.can_be_removed) {
{
if (notification.can_be_removed)
{
notification.state = NotificationStatus::FINISHED; notification.state = NotificationStatus::FINISHED;
} }
} }
@@ -300,11 +269,9 @@ void Notifier::clearNotifications()
} }
// Obtiene los códigos de las notificaciones // Obtiene los códigos de las notificaciones
std::vector<std::string> Notifier::getCodes() std::vector<std::string> Notifier::getCodes() {
{
std::vector<std::string> codes; std::vector<std::string> codes;
for (const auto &notification : notifications_) for (const auto& notification : notifications_) {
{
codes.emplace_back(notification.code); codes.emplace_back(notification.code);
} }
return codes; return codes;

127
source/ui/notifier.h Normal file
View File

@@ -0,0 +1,127 @@
#pragma once
#include <SDL3/SDL_rect.h> // Para SDL_Rect
#include <SDL3/SDL_stdinc.h> // Para Uint32, Uint8
#include <memory> // Para shared_ptr
#include <string> // Para string, basic_string
#include <vector> // Para vector
class SSprite; // lines 8-8
class Surface; // lines 10-10
class Text; // lines 9-9
// Constantes
constexpr Uint32 DEFAULT_NOTIFICATION_DURATION = 2000;
constexpr Uint32 CHEEVO_NOTIFICATION_DURATION = 4000;
// Justificado para las notificaciones
enum class NotificationText {
LEFT,
CENTER,
};
class Notifier {
private:
// Constantes
static constexpr int ICON_SIZE_ = 16;
static constexpr int PADDING_OUT_ = 0;
// [SINGLETON] Objeto notifier
static Notifier* notifier_;
enum class NotificationStatus {
RISING,
STAY,
VANISHING,
FINISHED,
};
enum class NotificationShape {
ROUNDED,
SQUARED,
};
struct Notification {
std::shared_ptr<Surface> surface; // Superficie asociada a la notificación
std::shared_ptr<SSprite> sprite; // Sprite asociado para gráficos o animaciones
std::vector<std::string> texts; // Lista de textos incluidos en la notificación
NotificationStatus state; // Estado actual de la notificación (RISING, SHOWING, etc.)
NotificationShape shape; // Forma de la notificación (ej. SQUARED o ROUNDED)
SDL_Rect rect; // Dimensiones y posición de la notificación en pantalla
int y; // Posición actual en el eje Y
int travel_dist; // Distancia a recorrer (por ejemplo, en animaciones)
std::string code; // Código identificador único para esta notificación
bool can_be_removed; // Indica si la notificación puede ser eliminada
int height; // Altura de la notificación
Uint32 start_time; // Momento en que se creó la notificación
Uint32 elapsed_time; // Tiempo transcurrido desde la creación
Uint32 display_duration; // Duración total para mostrar la notificación
// Constructor
explicit Notification()
: surface(nullptr), // Inicializar superficie como nula
sprite(nullptr), // Inicializar sprite como nulo
texts(), // Inicializar lista de textos vacía
state(NotificationStatus::RISING), // Estado inicial como "RISING"
shape(NotificationShape::SQUARED), // Forma inicial como "SQUARED"
rect{0, 0, 0, 0}, // Rectángulo inicial vacío
y(0), // Posición Y inicializada a 0
travel_dist(0), // Distancia inicializada a 0
code(""), // Código identificador vacío
can_be_removed(true), // Inicialmente se puede eliminar
height(0), // Altura inicializada a 0
start_time(0), // Tiempo de creación inicializado a 0
elapsed_time(0), // Tiempo transcurrido inicializado a 0
display_duration(0) // Duración inicializada a 0
{
}
};
std::shared_ptr<Surface> icon_surface_; // Textura para los iconos de las notificaciones
std::shared_ptr<Text> text_; // Objeto para dibujar texto
// Variables
Uint8 bg_color_; // Color de fondo de las notificaciones
std::vector<Notification> notifications_; // La lista de notificaciones activas
bool stack_; // Indica si las notificaciones se apilan
bool has_icons_; // Indica si el notificador tiene textura para iconos
// Elimina las notificaciones finalizadas
void clearFinishedNotifications();
// Finaliza y elimnina todas las notificaciones activas
void clearNotifications();
// [SINGLETON] Ahora el constructor y el destructor son privados, para no poder crear objetos notifier desde fuera
// Constructor
Notifier(const std::string& icon_file, const std::string& text);
// Destructor
~Notifier() = default;
public:
// [SINGLETON] Crearemos el objeto con esta función estática
static void init(const std::string& icon_file, const std::string& text);
// [SINGLETON] Destruiremos el objeto con esta función estática
static void destroy();
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
static Notifier* get();
// Dibuja las notificaciones por pantalla
void render();
// Actualiza el estado de las notificaiones
void update();
// Muestra una notificación de texto por pantalla
void show(std::vector<std::string> texts, NotificationText text_is = NotificationText::LEFT, Uint32 display_duration = DEFAULT_NOTIFICATION_DURATION, int icon = -1, bool can_be_removed = true, const std::string& code = std::string());
// Indica si hay notificaciones activas
bool isActive();
// Obtiene los códigos de las notificaciones
std::vector<std::string> getCodes();
};

View File

@@ -1,5 +1,7 @@
#include "utils.h" #include "utils.h"
#include <stdlib.h> // Para abs #include <stdlib.h> // Para abs
#include <algorithm> // Para find, transform #include <algorithm> // Para find, transform
#include <cctype> // Para tolower #include <cctype> // Para tolower
#include <cmath> // Para round, abs #include <cmath> // Para round, abs
@@ -9,27 +11,25 @@
#include <string> // Para basic_string, string, char_traits, allocator #include <string> // Para basic_string, string, char_traits, allocator
#include <unordered_map> // Para unordered_map, operator==, _Node_const_iter... #include <unordered_map> // Para unordered_map, operator==, _Node_const_iter...
#include <utility> // Para pair #include <utility> // Para pair
#include "jail_audio.h" // Para JA_GetMusicState, JA_Music_state, JA_PlayMusic #include "jail_audio.h" // Para JA_GetMusicState, JA_Music_state, JA_PlayMusic
#include "resource.h" // Para Resource #include "resource.h" // Para Resource
// Calcula el cuadrado de la distancia entre dos puntos // Calcula el cuadrado de la distancia entre dos puntos
double distanceSquared(int x1, int y1, int x2, int y2) double distanceSquared(int x1, int y1, int x2, int y2) {
{
const int deltaX = x2 - x1; const int deltaX = x2 - x1;
const int deltaY = y2 - y1; const int deltaY = y2 - y1;
return deltaX * deltaX + deltaY * deltaY; return deltaX * deltaX + deltaY * deltaY;
} }
// Detector de colisiones entre dos circulos // Detector de colisiones entre dos circulos
bool checkCollision(const Circle &a, const Circle &b) bool checkCollision(const Circle& a, const Circle& b) {
{
// Calcula el radio total al cuadrado // Calcula el radio total al cuadrado
int totalRadiusSquared = a.r + b.r; int totalRadiusSquared = a.r + b.r;
totalRadiusSquared = totalRadiusSquared * totalRadiusSquared; totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;
// Si la distancia entre el centro de los circulos es inferior a la suma de sus radios // Si la distancia entre el centro de los circulos es inferior a la suma de sus radios
if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared)) if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared)) {
{
// Los circulos han colisionado // Los circulos han colisionado
return true; return true;
} }
@@ -39,42 +39,30 @@ bool checkCollision(const Circle &a, const Circle &b)
} }
// Detector de colisiones entre un circulo y un rectangulo // Detector de colisiones entre un circulo y un rectangulo
bool checkCollision(const Circle &a, const SDL_Rect &b) bool checkCollision(const Circle& a, const SDL_Rect& b) {
{
// Closest point on collision box // Closest point on collision box
int cX, cY; int cX, cY;
// Find closest x offset // Find closest x offset
if (a.x < b.x) if (a.x < b.x) {
{
cX = b.x; cX = b.x;
} } else if (a.x > b.x + b.w) {
else if (a.x > b.x + b.w)
{
cX = b.x + b.w; cX = b.x + b.w;
} } else {
else
{
cX = a.x; cX = a.x;
} }
// Find closest y offset // Find closest y offset
if (a.y < b.y) if (a.y < b.y) {
{
cY = b.y; cY = b.y;
} } else if (a.y > b.y + b.h) {
else if (a.y > b.y + b.h)
{
cY = b.y + b.h; cY = b.y + b.h;
} } else {
else
{
cY = a.y; cY = a.y;
} }
// If the closest point is inside the circle_t // If the closest point is inside the circle_t
if (distanceSquared(a.x, a.y, cX, cY) < a.r * a.r) if (distanceSquared(a.x, a.y, cX, cY) < a.r * a.r) {
{
// This box and the circle_t have collided // This box and the circle_t have collided
return true; return true;
} }
@@ -84,8 +72,7 @@ bool checkCollision(const Circle &a, const SDL_Rect &b)
} }
// Detector de colisiones entre dos rectangulos // Detector de colisiones entre dos rectangulos
bool checkCollision(const SDL_Rect &a, const SDL_Rect &b) bool checkCollision(const SDL_Rect& a, const SDL_Rect& b) {
{
// Calcula las caras del rectangulo a // Calcula las caras del rectangulo a
const int leftA = a.x; const int leftA = a.x;
const int rightA = a.x + a.w; const int rightA = a.x + a.w;
@@ -99,23 +86,19 @@ bool checkCollision(const SDL_Rect &a, const SDL_Rect &b)
const int bottomB = b.y + b.h; const int bottomB = b.y + b.h;
// Si cualquiera de las caras de a está fuera de b // Si cualquiera de las caras de a está fuera de b
if (bottomA <= topB) if (bottomA <= topB) {
{
return false; return false;
} }
if (topA >= bottomB) if (topA >= bottomB) {
{
return false; return false;
} }
if (rightA <= leftB) if (rightA <= leftB) {
{
return false; return false;
} }
if (leftA >= rightB) if (leftA >= rightB) {
{
return false; return false;
} }
@@ -124,29 +107,24 @@ bool checkCollision(const SDL_Rect &a, const SDL_Rect &b)
} }
// Detector de colisiones entre un punto y un rectangulo // Detector de colisiones entre un punto y un rectangulo
bool checkCollision(const SDL_Point &p, const SDL_Rect &r) bool checkCollision(const SDL_Point& p, const SDL_Rect& r) {
{
// Comprueba si el punto está a la izquierda del rectangulo // Comprueba si el punto está a la izquierda del rectangulo
if (p.x < r.x) if (p.x < r.x) {
{
return false; return false;
} }
// Comprueba si el punto está a la derecha del rectangulo // Comprueba si el punto está a la derecha del rectangulo
if (p.x > r.x + r.w) if (p.x > r.x + r.w) {
{
return false; return false;
} }
// Comprueba si el punto está por encima del rectangulo // Comprueba si el punto está por encima del rectangulo
if (p.y < r.y) if (p.y < r.y) {
{
return false; return false;
} }
// Comprueba si el punto está por debajo del rectangulo // Comprueba si el punto está por debajo del rectangulo
if (p.y > r.y + r.h) if (p.y > r.y + r.h) {
{
return false; return false;
} }
@@ -155,29 +133,24 @@ bool checkCollision(const SDL_Point &p, const SDL_Rect &r)
} }
// Detector de colisiones entre una linea horizontal y un rectangulo // Detector de colisiones entre una linea horizontal y un rectangulo
bool checkCollision(const LineHorizontal &l, const SDL_Rect &r) bool checkCollision(const LineHorizontal& l, const SDL_Rect& r) {
{
// Comprueba si la linea esta por encima del rectangulo // Comprueba si la linea esta por encima del rectangulo
if (l.y < r.y) if (l.y < r.y) {
{
return false; return false;
} }
// Comprueba si la linea esta por debajo del rectangulo // Comprueba si la linea esta por debajo del rectangulo
if (l.y >= r.y + r.h) if (l.y >= r.y + r.h) {
{
return false; return false;
} }
// Comprueba si el inicio de la linea esta a la derecha del rectangulo // Comprueba si el inicio de la linea esta a la derecha del rectangulo
if (l.x1 >= r.x + r.w) if (l.x1 >= r.x + r.w) {
{
return false; return false;
} }
// Comprueba si el final de la linea esta a la izquierda del rectangulo // Comprueba si el final de la linea esta a la izquierda del rectangulo
if (l.x2 < r.x) if (l.x2 < r.x) {
{
return false; return false;
} }
@@ -186,29 +159,24 @@ bool checkCollision(const LineHorizontal &l, const SDL_Rect &r)
} }
// Detector de colisiones entre una linea vertical y un rectangulo // Detector de colisiones entre una linea vertical y un rectangulo
bool checkCollision(const LineVertical &l, const SDL_Rect &r) bool checkCollision(const LineVertical& l, const SDL_Rect& r) {
{
// Comprueba si la linea esta por la izquierda del rectangulo // Comprueba si la linea esta por la izquierda del rectangulo
if (l.x < r.x) if (l.x < r.x) {
{
return false; return false;
} }
// Comprueba si la linea esta por la derecha del rectangulo // Comprueba si la linea esta por la derecha del rectangulo
if (l.x >= r.x + r.w) if (l.x >= r.x + r.w) {
{
return false; return false;
} }
// Comprueba si el inicio de la linea esta debajo del rectangulo // Comprueba si el inicio de la linea esta debajo del rectangulo
if (l.y1 >= r.y + r.h) if (l.y1 >= r.y + r.h) {
{
return false; return false;
} }
// Comprueba si el final de la linea esta encima del rectangulo // Comprueba si el final de la linea esta encima del rectangulo
if (l.y2 < r.y) if (l.y2 < r.y) {
{
return false; return false;
} }
@@ -217,29 +185,24 @@ bool checkCollision(const LineVertical &l, const SDL_Rect &r)
} }
// Detector de colisiones entre una linea horizontal y un punto // Detector de colisiones entre una linea horizontal y un punto
bool checkCollision(const LineHorizontal &l, const SDL_Point &p) bool checkCollision(const LineHorizontal& l, const SDL_Point& p) {
{
// Comprueba si el punto esta sobre la linea // Comprueba si el punto esta sobre la linea
if (p.y > l.y) if (p.y > l.y) {
{
return false; return false;
} }
// Comprueba si el punto esta bajo la linea // Comprueba si el punto esta bajo la linea
if (p.y < l.y) if (p.y < l.y) {
{
return false; return false;
} }
// Comprueba si el punto esta a la izquierda de la linea // Comprueba si el punto esta a la izquierda de la linea
if (p.x < l.x1) if (p.x < l.x1) {
{
return false; return false;
} }
// Comprueba si el punto esta a la derecha de la linea // Comprueba si el punto esta a la derecha de la linea
if (p.x > l.x2) if (p.x > l.x2) {
{
return false; return false;
} }
@@ -248,8 +211,7 @@ bool checkCollision(const LineHorizontal &l, const SDL_Point &p)
} }
// Detector de colisiones entre dos lineas // Detector de colisiones entre dos lineas
SDL_Point checkCollision(const Line &l1, const Line &l2) SDL_Point checkCollision(const Line& l1, const Line& l2) {
{
const float x1 = l1.x1; const float x1 = l1.x1;
const float y1 = l1.y1; const float y1 = l1.y1;
const float x2 = l1.x2; const float x2 = l1.x2;
@@ -265,8 +227,7 @@ SDL_Point checkCollision(const Line &l1, const Line &l2)
float uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); float uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
// if uA and uB are between 0-1, lines are colliding // if uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
{
// Calcula la intersección // Calcula la intersección
const float x = x1 + (uA * (x2 - x1)); const float x = x1 + (uA * (x2 - x1));
const float y = y1 + (uA * (y2 - y1)); const float y = y1 + (uA * (y2 - y1));
@@ -277,8 +238,7 @@ SDL_Point checkCollision(const Line &l1, const Line &l2)
} }
// Detector de colisiones entre dos lineas // Detector de colisiones entre dos lineas
SDL_Point checkCollision(const LineDiagonal &l1, const LineVertical &l2) SDL_Point checkCollision(const LineDiagonal& l1, const LineVertical& l2) {
{
const float x1 = l1.x1; const float x1 = l1.x1;
const float y1 = l1.y1; const float y1 = l1.y1;
const float x2 = l1.x2; const float x2 = l1.x2;
@@ -294,8 +254,7 @@ SDL_Point checkCollision(const LineDiagonal &l1, const LineVertical &l2)
float uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)); float uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
// if uA and uB are between 0-1, lines are colliding // if uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
{
// Calcula la intersección // Calcula la intersección
const float x = x1 + (uA * (x2 - x1)); const float x = x1 + (uA * (x2 - x1));
const float y = y1 + (uA * (y2 - y1)); const float y = y1 + (uA * (y2 - y1));
@@ -306,12 +265,10 @@ SDL_Point checkCollision(const LineDiagonal &l1, const LineVertical &l2)
} }
// Normaliza una linea diagonal // Normaliza una linea diagonal
void normalizeLine(LineDiagonal &l) void normalizeLine(LineDiagonal& l) {
{
// Las lineas diagonales van de izquierda a derecha // Las lineas diagonales van de izquierda a derecha
// x2 mayor que x1 // x2 mayor que x1
if (l.x2 < l.x1) if (l.x2 < l.x1) {
{
const int x = l.x1; const int x = l.x1;
const int y = l.y1; const int y = l.y1;
l.x1 = l.x2; l.x1 = l.x2;
@@ -322,35 +279,29 @@ void normalizeLine(LineDiagonal &l)
} }
// Detector de colisiones entre un punto y una linea diagonal // Detector de colisiones entre un punto y una linea diagonal
bool checkCollision(const SDL_Point &p, const LineDiagonal &l) bool checkCollision(const SDL_Point& p, const LineDiagonal& l) {
{
// Comprueba si el punto está en alineado con la linea // Comprueba si el punto está en alineado con la linea
if (abs(p.x - l.x1) != abs(p.y - l.y1)) if (abs(p.x - l.x1) != abs(p.y - l.y1)) {
{
return false; return false;
} }
// Comprueba si está a la derecha de la linea // Comprueba si está a la derecha de la linea
if (p.x > l.x1 && p.x > l.x2) if (p.x > l.x1 && p.x > l.x2) {
{
return false; return false;
} }
// Comprueba si está a la izquierda de la linea // Comprueba si está a la izquierda de la linea
if (p.x < l.x1 && p.x < l.x2) if (p.x < l.x1 && p.x < l.x2) {
{
return false; return false;
} }
// Comprueba si está por encima de la linea // Comprueba si está por encima de la linea
if (p.y > l.y1 && p.y > l.y2) if (p.y > l.y1 && p.y > l.y2) {
{
return false; return false;
} }
// Comprueba si está por debajo de la linea // Comprueba si está por debajo de la linea
if (p.y < l.y1 && p.y < l.y2) if (p.y < l.y1 && p.y < l.y2) {
{
return false; return false;
} }
@@ -359,8 +310,7 @@ bool checkCollision(const SDL_Point &p, const LineDiagonal &l)
} }
// Convierte una cadena a un indice de la paleta // Convierte una cadena a un indice de la paleta
Uint8 stringToColor(const std::string &str) Uint8 stringToColor(const std::string& str) {
{
// Mapas de colores para cada paleta // Mapas de colores para cada paleta
static const std::unordered_map<std::string, Uint8> paletteMap = { static const std::unordered_map<std::string, Uint8> paletteMap = {
{"black", 0}, {"black", 0},
@@ -391,47 +341,37 @@ Uint8 stringToColor(const std::string &str)
// Busca el color en el mapa // Busca el color en el mapa
auto it = paletteMap.find(str); auto it = paletteMap.find(str);
if (it != paletteMap.end()) if (it != paletteMap.end()) {
{
return it->second; return it->second;
} } else {
else
{
// Si no se encuentra el color, devolvemos negro por defecto // Si no se encuentra el color, devolvemos negro por defecto
return 0; return 0;
} }
} }
// Convierte una cadena a un entero de forma segura // Convierte una cadena a un entero de forma segura
int safeStoi(const std::string &value, int defaultValue) int safeStoi(const std::string& value, int defaultValue) {
{ try {
try
{
return std::stoi(value); return std::stoi(value);
} } catch (const std::exception&) {
catch (const std::exception &)
{
return defaultValue; return defaultValue;
} }
} }
// Convierte una cadena a un booleano // Convierte una cadena a un booleano
bool stringToBool(const std::string &str) bool stringToBool(const std::string& str) {
{
std::string lowerStr = str; std::string lowerStr = str;
std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower); std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower);
return (lowerStr == "true" || lowerStr == "1" || lowerStr == "yes" || lowerStr == "on"); return (lowerStr == "true" || lowerStr == "1" || lowerStr == "yes" || lowerStr == "on");
} }
// Convierte un booleano a una cadena // Convierte un booleano a una cadena
std::string boolToString(bool value) std::string boolToString(bool value) {
{
return value ? "1" : "0"; return value ? "1" : "0";
} }
// Compara dos colores // Compara dos colores
bool colorAreEqual(Color color1, Color color2) bool colorAreEqual(Color color1, Color color2) {
{
const bool r = color1.r == color2.r; const bool r = color1.r == color2.r;
const bool g = color1.g == color2.g; const bool g = color1.g == color2.g;
const bool b = color1.b == color2.b; const bool b = color1.b == color2.b;
@@ -440,37 +380,32 @@ bool colorAreEqual(Color color1, Color color2)
} }
// Función para convertir un string a minúsculas // Función para convertir un string a minúsculas
std::string toLower(const std::string &str) std::string toLower(const std::string& str) {
{
std::string lower_str = str; std::string lower_str = str;
std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), ::tolower); std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), ::tolower);
return lower_str; return lower_str;
} }
// Función para convertir un string a mayúsculas // Función para convertir un string a mayúsculas
std::string toUpper(const std::string &str) std::string toUpper(const std::string& str) {
{
std::string upper_str = str; std::string upper_str = str;
std::transform(upper_str.begin(), upper_str.end(), upper_str.begin(), ::toupper); std::transform(upper_str.begin(), upper_str.end(), upper_str.begin(), ::toupper);
return upper_str; return upper_str;
} }
// Obtiene el nombre de un fichero a partir de una ruta completa // Obtiene el nombre de un fichero a partir de una ruta completa
std::string getFileName(const std::string &path) std::string getFileName(const std::string& path) {
{
return std::filesystem::path(path).filename().string(); return std::filesystem::path(path).filename().string();
} }
// Obtiene la ruta eliminando el nombre del fichero // Obtiene la ruta eliminando el nombre del fichero
std::string getPath(const std::string &full_path) std::string getPath(const std::string& full_path) {
{
std::filesystem::path path(full_path); std::filesystem::path path(full_path);
return path.parent_path().string(); return path.parent_path().string();
} }
// Imprime por pantalla una linea de texto de tamaño fijo rellena con puntos // Imprime por pantalla una linea de texto de tamaño fijo rellena con puntos
void printWithDots(const std::string &text1, const std::string &text2, const std::string &text3) void printWithDots(const std::string& text1, const std::string& text2, const std::string& text3) {
{
std::cout.setf(std::ios::left, std::ios::adjustfield); std::cout.setf(std::ios::left, std::ios::adjustfield);
std::cout << text1; std::cout << text1;
@@ -482,26 +417,22 @@ void printWithDots(const std::string &text1, const std::string &text2, const std
} }
// Comprueba si una vector contiene una cadena // Comprueba si una vector contiene una cadena
bool stringInVector(const std::vector<std::string> &vec, const std::string &str) bool stringInVector(const std::vector<std::string>& vec, const std::string& str) {
{
return std::find(vec.begin(), vec.end(), str) != vec.end(); return std::find(vec.begin(), vec.end(), str) != vec.end();
} }
// Hace sonar la música // Hace sonar la música
void playMusic(const std::string &music_path) void playMusic(const std::string& music_path) {
{
// Si la música no está sonando // Si la música no está sonando
if (JA_GetMusicState() == JA_MUSIC_INVALID || JA_GetMusicState() == JA_MUSIC_STOPPED) if (JA_GetMusicState() == JA_MUSIC_INVALID || JA_GetMusicState() == JA_MUSIC_STOPPED) {
{
JA_PlayMusic(Resource::get()->getMusic(music_path)); JA_PlayMusic(Resource::get()->getMusic(music_path));
} }
} }
// Rellena una textura de un color // Rellena una textura de un color
void fillTextureWithColor(SDL_Renderer *renderer, SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a) void fillTextureWithColor(SDL_Renderer* renderer, SDL_Texture* texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a) {
{
// Guardar el render target actual // Guardar el render target actual
SDL_Texture *previous_target = SDL_GetRenderTarget(renderer); SDL_Texture* previous_target = SDL_GetRenderTarget(renderer);
// Establecer la textura como el render target // Establecer la textura como el render target
SDL_SetRenderTarget(renderer, texture); SDL_SetRenderTarget(renderer, texture);

View File

@@ -1,13 +1,13 @@
#pragma once #pragma once
#include <SDL2/SDL_rect.h> // Para SDL_Rect, SDL_Point #include <SDL3/SDL_rect.h> // Para SDL_Rect, SDL_Point
#include <SDL2/SDL_render.h> // Para SDL_Renderer, SDL_Texture #include <SDL3/SDL_render.h> // Para SDL_Renderer, SDL_Texture
#include <SDL2/SDL_stdinc.h> // Para Uint8 #include <SDL3/SDL_stdinc.h> // Para Uint8
#include <string> // Para string #include <string> // Para string
#include <vector> // Para vector #include <vector> // Para vector
enum class PaletteColor : Uint8 enum class PaletteColor : Uint8 {
{
BLACK = 0, BLACK = 0,
BRIGHT_BLACK = 1, BRIGHT_BLACK = 1,
@@ -36,96 +36,95 @@ enum class PaletteColor : Uint8
}; };
// Estructura para definir un circulo // Estructura para definir un circulo
struct Circle struct Circle {
{
int x; int x;
int y; int y;
int r; int r;
}; };
// Estructura para definir una linea horizontal // Estructura para definir una linea horizontal
struct LineHorizontal struct LineHorizontal {
{
int x1, x2, y; int x1, x2, y;
}; };
// Estructura para definir una linea vertical // Estructura para definir una linea vertical
struct LineVertical struct LineVertical {
{
int x, y1, y2; int x, y1, y2;
}; };
// Estructura para definir una linea diagonal // Estructura para definir una linea diagonal
struct LineDiagonal struct LineDiagonal {
{
int x1, y1, x2, y2; int x1, y1, x2, y2;
}; };
// Estructura para definir una linea // Estructura para definir una linea
struct Line struct Line {
{
int x1, y1, x2, y2; int x1, y1, x2, y2;
}; };
// Estructura para definir un color // Estructura para definir un color
struct Color struct Color {
{
Uint8 r; Uint8 r;
Uint8 g; Uint8 g;
Uint8 b; Uint8 b;
// Constructor por defecto // Constructor por defecto
Color() : r(0), g(0), b(0) {} Color()
: r(0),
g(0),
b(0) {}
// Constructor // Constructor
Color(Uint8 red, Uint8 green, Uint8 blue) Color(Uint8 red, Uint8 green, Uint8 blue)
: r(red), g(green), b(blue) {} : r(red),
g(green),
b(blue) {}
}; };
// Calcula el cuadrado de la distancia entre dos puntos // Calcula el cuadrado de la distancia entre dos puntos
double distanceSquared(int x1, int y1, int x2, int y2); double distanceSquared(int x1, int y1, int x2, int y2);
// Detector de colisiones entre dos circulos // Detector de colisiones entre dos circulos
bool checkCollision(const Circle &a, const Circle &b); bool checkCollision(const Circle& a, const Circle& b);
// Detector de colisiones entre un circulo y un rectangulo // Detector de colisiones entre un circulo y un rectangulo
bool checkCollision(const Circle &a, const SDL_Rect &b); bool checkCollision(const Circle& a, const SDL_Rect& b);
// Detector de colisiones entre un dos rectangulos // Detector de colisiones entre un dos rectangulos
bool checkCollision(const SDL_Rect &a, const SDL_Rect &b); bool checkCollision(const SDL_Rect& a, const SDL_Rect& b);
// Detector de colisiones entre un punto y un rectangulo // Detector de colisiones entre un punto y un rectangulo
bool checkCollision(const SDL_Point &p, const SDL_Rect &r); bool checkCollision(const SDL_Point& p, const SDL_Rect& r);
// Detector de colisiones entre una linea horizontal y un rectangulo // Detector de colisiones entre una linea horizontal y un rectangulo
bool checkCollision(const LineHorizontal &l, const SDL_Rect &r); bool checkCollision(const LineHorizontal& l, const SDL_Rect& r);
// Detector de colisiones entre una linea vertical y un rectangulo // Detector de colisiones entre una linea vertical y un rectangulo
bool checkCollision(const LineVertical &l, const SDL_Rect &r); bool checkCollision(const LineVertical& l, const SDL_Rect& r);
// Detector de colisiones entre una linea horizontal y un punto // Detector de colisiones entre una linea horizontal y un punto
bool checkCollision(const LineHorizontal &l, const SDL_Point &p); bool checkCollision(const LineHorizontal& l, const SDL_Point& p);
// Detector de colisiones entre dos lineas // Detector de colisiones entre dos lineas
SDL_Point checkCollision(const Line &l1, const Line &l2); SDL_Point checkCollision(const Line& l1, const Line& l2);
// Detector de colisiones entre dos lineas // Detector de colisiones entre dos lineas
SDL_Point checkCollision(const LineDiagonal &l1, const LineVertical &l2); SDL_Point checkCollision(const LineDiagonal& l1, const LineVertical& l2);
// Detector de colisiones entre un punto y una linea diagonal // Detector de colisiones entre un punto y una linea diagonal
bool checkCollision(const SDL_Point &p, const LineDiagonal &l); bool checkCollision(const SDL_Point& p, const LineDiagonal& l);
// Normaliza una linea diagonal // Normaliza una linea diagonal
void normalizeLine(LineDiagonal &l); void normalizeLine(LineDiagonal& l);
// Devuelve un Color a partir de un string // Devuelve un Color a partir de un string
Uint8 stringToColor(const std::string &str); Uint8 stringToColor(const std::string& str);
// Convierte una cadena a un entero de forma segura // Convierte una cadena a un entero de forma segura
int safeStoi(const std::string &value, int defaultValue = 0); int safeStoi(const std::string& value, int defaultValue = 0);
// Convierte una cadena a un booleano // Convierte una cadena a un booleano
bool stringToBool(const std::string &str); bool stringToBool(const std::string& str);
// Convierte un booleano a una cadena // Convierte un booleano a una cadena
std::string boolToString(bool value); std::string boolToString(bool value);
@@ -134,25 +133,25 @@ std::string boolToString(bool value);
bool colorAreEqual(Color color1, Color color2); bool colorAreEqual(Color color1, Color color2);
// Convierte una cadena a minusculas // Convierte una cadena a minusculas
std::string toLower(const std::string &str); std::string toLower(const std::string& str);
// Convierte una cadena a mayúsculas // Convierte una cadena a mayúsculas
std::string toUpper(const std::string &str); std::string toUpper(const std::string& str);
// Obtiene el nombre de un fichero a partir de una ruta // Obtiene el nombre de un fichero a partir de una ruta
std::string getFileName(const std::string &path); std::string getFileName(const std::string& path);
// Obtiene la ruta eliminando el nombre del fichero // Obtiene la ruta eliminando el nombre del fichero
std::string getPath(const std::string &full_path); std::string getPath(const std::string& full_path);
// Imprime por pantalla una linea de texto de tamaño fijo rellena con puntos // Imprime por pantalla una linea de texto de tamaño fijo rellena con puntos
void printWithDots(const std::string &text1, const std::string &text2, const std::string &text3); void printWithDots(const std::string& text1, const std::string& text2, const std::string& text3);
// Comprueba si una vector contiene una cadena // Comprueba si una vector contiene una cadena
bool stringInVector(const std::vector<std::string> &vec, const std::string &str); bool stringInVector(const std::vector<std::string>& vec, const std::string& str);
// Hace sonar la música // Hace sonar la música
void playMusic(const std::string &music_path); void playMusic(const std::string& music_path);
// Rellena una textura de un color // Rellena una textura de un color
void fillTextureWithColor(SDL_Renderer *renderer, SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a); void fillTextureWithColor(SDL_Renderer* renderer, SDL_Texture* texture, Uint8 r, Uint8 g, Uint8 b, Uint8 a);

6
source/version.h.in Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
namespace Version {
constexpr const char* GIT_HASH = "@GIT_HASH@";
constexpr const char* APP_NAME = "Coffee Crisis Arcade Edition";
}