primer commit
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
.vscode/
|
||||
build/
|
||||
|
||||
# ---> C++
|
||||
# Prerequisites
|
||||
*.d
|
||||
@@ -65,7 +68,8 @@ $RECYCLE.BIN/
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
49
CMakeLists.txt
Normal file
49
CMakeLists.txt
Normal file
@@ -0,0 +1,49 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(logo_01)
|
||||
|
||||
# Establecer el estándar de C++
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Opciones comunes de compilación
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Os -ffunction-sections -fdata-sections")
|
||||
|
||||
# Buscar SDL3 automáticamente
|
||||
find_package(SDL3 REQUIRED)
|
||||
|
||||
# Si no se encuentra SDL3, generar un error
|
||||
if (NOT SDL3_FOUND)
|
||||
message(FATAL_ERROR "SDL3 no encontrado. Por favor, verifica su instalación.")
|
||||
endif()
|
||||
|
||||
# Archivos fuente
|
||||
file(GLOB SOURCE_FILES source/*.cpp)
|
||||
|
||||
# Comprobar si se encontraron archivos fuente
|
||||
if(NOT SOURCE_FILES)
|
||||
message(FATAL_ERROR "No se encontraron archivos fuente en el directorio 'source/'. Verifica la ruta.")
|
||||
endif()
|
||||
|
||||
# Detectar la plataforma y configuraciones específicas
|
||||
if(WIN32)
|
||||
set(PLATFORM windows)
|
||||
set(LINK_LIBS ${SDL3_LIBRARIES} mingw32 ws2_32)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
set(PLATFORM linux)
|
||||
set(LINK_LIBS ${SDL3_LIBRARIES})
|
||||
elseif(APPLE)
|
||||
set(PLATFORM macos)
|
||||
set(LINK_LIBS ${SDL3_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Incluir directorios de SDL3
|
||||
include_directories(${SDL3_INCLUDE_DIRS})
|
||||
|
||||
# Añadir el ejecutable reutilizando el nombre del proyecto
|
||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
|
||||
|
||||
# Especificar la ubicación del ejecutable (en la raíz del proyecto)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# Enlazar las bibliotecas necesarias
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIBS})
|
||||
22
Makefile
Normal file
22
Makefile
Normal file
@@ -0,0 +1,22 @@
|
||||
# Variables comunes
|
||||
SOURCE := source/*.cpp
|
||||
EXECUTABLE_NAME := logo_01
|
||||
CXXFLAGS := -std=c++20 -Wall -Os -ffunction-sections -fdata-sections # Opciones comunes de compilación
|
||||
LDFLAGS := -lSDL3 # Flags de enlace comunes
|
||||
OUTPUT_EXT :=
|
||||
|
||||
# Detectar plataforma y configurar
|
||||
ifeq ($(OS),Windows_NT)
|
||||
LDFLAGS += -lmingw32 -lws2_32
|
||||
OUTPUT_EXT := .exe
|
||||
else
|
||||
OUTPUT_EXT := .out
|
||||
endif
|
||||
|
||||
# Regla principal: compilar el ejecutable
|
||||
all:
|
||||
$(CXX) $(SOURCE) $(CXXFLAGS) $(LDFLAGS) -o $(EXECUTABLE_NAME)$(OUTPUT_EXT)
|
||||
|
||||
# Regla para limpiar archivos generados
|
||||
clean:
|
||||
rm -f $(EXECUTABLE_NAME)$(OUTPUT_EXT)
|
||||
BIN
jailgames.gif
Normal file
BIN
jailgames.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 478 B |
5
jailgames.pal
Normal file
5
jailgames.pal
Normal file
@@ -0,0 +1,5 @@
|
||||
JASC-PAL
|
||||
0100
|
||||
2
|
||||
0 0 0
|
||||
255 255 255
|
||||
128
source/audio.cpp
Normal file
128
source/audio.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "audio.h"
|
||||
#include "jail_audio.h"
|
||||
#include "options.h"
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
// [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado
|
||||
Audio *Audio::audio_ = nullptr;
|
||||
|
||||
// [SINGLETON] Crearemos el objeto asset con esta función estática
|
||||
void Audio::init()
|
||||
{
|
||||
Audio::audio_ = new Audio();
|
||||
}
|
||||
|
||||
// [SINGLETON] Destruiremos el objeto asset con esta función estática
|
||||
void Audio::destroy()
|
||||
{
|
||||
delete Audio::audio_;
|
||||
}
|
||||
|
||||
// [SINGLETON] Con este método obtenemos el objeto asset y podemos trabajar con él
|
||||
Audio *Audio::get()
|
||||
{
|
||||
return Audio::audio_;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
Audio::Audio()
|
||||
{
|
||||
// Inicializa SDL
|
||||
if (!SDL_Init(SDL_INIT_AUDIO))
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_AUDIO could not initialize! SDL Error: %s", SDL_GetError());
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_AUDIO: INITIALIZING\n");
|
||||
|
||||
JA_Init(48000, SDL_AUDIO_S16LE, 2);
|
||||
enable(options.audio.enabled);
|
||||
setMusicVolume(options.audio.music.volume);
|
||||
setSoundVolume(options.audio.sound.volume);
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "** SDL_AUDIO: INITIALIZATION COMPLETE\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor
|
||||
Audio::~Audio()
|
||||
{
|
||||
JA_Quit();
|
||||
}
|
||||
|
||||
// Reproduce la música
|
||||
void Audio::playMusic(JA_Music_t *music, const int loop)
|
||||
{
|
||||
if (enabled_ && music_enabled_)
|
||||
{
|
||||
JA_PlayMusic(music, loop);
|
||||
}
|
||||
}
|
||||
|
||||
// Pausa la música
|
||||
void Audio::pauseMusic()
|
||||
{
|
||||
if (enabled_ && music_enabled_)
|
||||
{
|
||||
JA_PauseMusic();
|
||||
}
|
||||
}
|
||||
|
||||
// Detiene la música
|
||||
void Audio::stopMusic()
|
||||
{
|
||||
if (enabled_ && music_enabled_)
|
||||
{
|
||||
JA_StopMusic();
|
||||
}
|
||||
}
|
||||
|
||||
// Reproduce un sonido
|
||||
void Audio::playSound(JA_Sound_t *sound)
|
||||
{
|
||||
if (enabled_ && sound_enabled_)
|
||||
{
|
||||
JA_PlaySound(sound);
|
||||
}
|
||||
}
|
||||
|
||||
// Detiene todos los sonidos
|
||||
void Audio::stopAllSounds()
|
||||
{
|
||||
if (enabled_ && sound_enabled_)
|
||||
{
|
||||
JA_StopChannel(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Realiza un fundido de salida de la música
|
||||
void Audio::fadeOutMusic(int milliseconds)
|
||||
{
|
||||
if (enabled_ && music_enabled_)
|
||||
{
|
||||
JA_FadeOutMusic(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
// Establece el volumen de los sonidos
|
||||
void Audio::setSoundVolume(int volume)
|
||||
{
|
||||
if (enabled_ && sound_enabled_)
|
||||
{
|
||||
volume = std::clamp(volume, 0, 100);
|
||||
const int COVERTED_VOLUME = static_cast<int>((volume / 100.0) * 128);
|
||||
JA_SetSoundVolume(COVERTED_VOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
// Establece el volumen de la música
|
||||
void Audio::setMusicVolume(int volume)
|
||||
{
|
||||
if (enabled_ && music_enabled_)
|
||||
{
|
||||
volume = std::clamp(volume, 0, 100);
|
||||
const int COVERTED_VOLUME = static_cast<int>((volume / 100.0) * 128);
|
||||
JA_SetMusicVolume(COVERTED_VOLUME);
|
||||
}
|
||||
}
|
||||
63
source/audio.h
Normal file
63
source/audio.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "jail_audio.h"
|
||||
|
||||
class Audio
|
||||
{
|
||||
private:
|
||||
// [SINGLETON] Objeto audio privado
|
||||
static Audio *audio_;
|
||||
|
||||
// Constructor
|
||||
Audio(); // Constructor privado para el patrón Singleton
|
||||
|
||||
// Destructor
|
||||
~Audio(); // Destructor privado para el patrón Singleton
|
||||
|
||||
// Variables
|
||||
bool enabled_ = true; // Indica si el audio está habilitado
|
||||
bool sound_enabled_ = true; // Indica si los sonidos están habilitados
|
||||
bool music_enabled_ = true; // Indica si la música está habilitada
|
||||
|
||||
public:
|
||||
// [SINGLETON] Crearemos el objeto con esta función estática
|
||||
static void init(); // Inicializa el objeto Singleton
|
||||
|
||||
// [SINGLETON] Destruiremos el objeto con esta función estática
|
||||
static void destroy(); // Destruye el objeto Singleton
|
||||
|
||||
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
|
||||
static Audio *get(); // Devuelve la instancia del Singleton
|
||||
|
||||
void playMusic(JA_Music_t *music, const int loop = -1); // Reproduce la música
|
||||
void pauseMusic(); // Pausa la música
|
||||
void stopMusic(); // Detiene la música
|
||||
|
||||
void playSound(JA_Sound_t *sound); // Reproduce un sonido
|
||||
void stopAllSounds(); // Detiene todos los sonidos
|
||||
|
||||
void fadeOutMusic(int milliseconds); // Realiza un fundido de salida de la música
|
||||
|
||||
// Audio
|
||||
void enable() { enabled_ = true; } // Habilita el audio
|
||||
void disable() { enabled_ = false; } // Deshabilita el audio
|
||||
void enable(bool value) { enabled_ = value; } // Habilita o deshabilita el audio
|
||||
void toggleEnabled() { enabled_ = !enabled_; } // Alterna el estado del audio
|
||||
|
||||
// Sound
|
||||
void enableSound() { sound_enabled_ = true; } // Habilita los sonidos
|
||||
void disableSound() { sound_enabled_ = false; } // Deshabilita los sonidos
|
||||
void enableSound(bool value) { sound_enabled_ = value; } // Habilita o deshabilita los sonidos
|
||||
void toggleSound() { sound_enabled_ = !sound_enabled_; } // Alterna el estado de los sonidos
|
||||
|
||||
// Music
|
||||
void enableMusic() { music_enabled_ = true; } // Habilita la música
|
||||
void disableMusic() { music_enabled_ = false; } // Deshabilita la música
|
||||
void enableMusic(bool value) { music_enabled_ = value; } // Habilita o deshabilita la música
|
||||
void toggleMusic() { music_enabled_ = !music_enabled_; } // Alterna el estado de la música
|
||||
|
||||
// Volume
|
||||
void setSoundVolume(int volume); // Establece el volumen de los sonidos
|
||||
void setMusicVolume(int volume); // Establece el volumen de la música
|
||||
};
|
||||
316
source/gif.cpp
Normal file
316
source/gif.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
#include "gif.h"
|
||||
#include <iostream> // Para std::cout
|
||||
#include <cstring> // Para memcpy, size_t
|
||||
#include <stdexcept> // Para runtime_error
|
||||
#include <string> // Para allocator, char_traits, operator==, basic_string
|
||||
|
||||
namespace GIF
|
||||
{
|
||||
|
||||
// Función inline para reemplazar el macro READ.
|
||||
// Actualiza el puntero 'buffer' tras copiar 'size' bytes a 'dst'.
|
||||
inline void readBytes(const uint8_t *&buffer, void *dst, size_t size)
|
||||
{
|
||||
std::memcpy(dst, buffer, size);
|
||||
buffer += size;
|
||||
}
|
||||
|
||||
void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out)
|
||||
{
|
||||
// Verifica que el code_length tenga un rango razonable.
|
||||
if (code_length < 2 || code_length > 12)
|
||||
{
|
||||
throw std::runtime_error("Invalid LZW code length");
|
||||
}
|
||||
|
||||
int i, bit;
|
||||
int prev = -1;
|
||||
std::vector<DictionaryEntry> dictionary;
|
||||
int dictionary_ind;
|
||||
unsigned int mask = 0x01;
|
||||
int reset_code_length = code_length;
|
||||
int clear_code = 1 << code_length;
|
||||
int stop_code = clear_code + 1;
|
||||
int match_len = 0;
|
||||
|
||||
// Inicializamos el diccionario con el tamaño correspondiente.
|
||||
dictionary.resize(1 << (code_length + 1));
|
||||
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++)
|
||||
{
|
||||
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
|
||||
dictionary[dictionary_ind].prev = -1;
|
||||
dictionary[dictionary_ind].len = 1;
|
||||
}
|
||||
dictionary_ind += 2; // Reservamos espacio para clear y stop codes
|
||||
|
||||
// Bucle principal: procesar el stream comprimido.
|
||||
while (input_length > 0)
|
||||
{
|
||||
int code = 0;
|
||||
// Lee (code_length + 1) bits para formar el código.
|
||||
for (i = 0; i < (code_length + 1); i++)
|
||||
{
|
||||
if (input_length <= 0)
|
||||
{
|
||||
throw std::runtime_error("Unexpected end of input in decompress");
|
||||
}
|
||||
bit = ((*input & mask) != 0) ? 1 : 0;
|
||||
mask <<= 1;
|
||||
if (mask == 0x100)
|
||||
{
|
||||
mask = 0x01;
|
||||
input++;
|
||||
input_length--;
|
||||
}
|
||||
code |= (bit << i);
|
||||
}
|
||||
|
||||
if (code == clear_code)
|
||||
{
|
||||
// Reinicia el diccionario.
|
||||
code_length = reset_code_length;
|
||||
dictionary.resize(1 << (code_length + 1));
|
||||
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++)
|
||||
{
|
||||
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
|
||||
dictionary[dictionary_ind].prev = -1;
|
||||
dictionary[dictionary_ind].len = 1;
|
||||
}
|
||||
dictionary_ind += 2;
|
||||
prev = -1;
|
||||
continue;
|
||||
}
|
||||
else if (code == stop_code)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (prev > -1 && code_length < 12)
|
||||
{
|
||||
if (code > dictionary_ind)
|
||||
{
|
||||
std::cerr << "code = " << std::hex << code
|
||||
<< ", but dictionary_ind = " << dictionary_ind << std::endl;
|
||||
throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
|
||||
}
|
||||
|
||||
int ptr;
|
||||
if (code == dictionary_ind)
|
||||
{
|
||||
ptr = prev;
|
||||
while (dictionary[ptr].prev != -1)
|
||||
ptr = dictionary[ptr].prev;
|
||||
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = code;
|
||||
while (dictionary[ptr].prev != -1)
|
||||
ptr = dictionary[ptr].prev;
|
||||
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
|
||||
}
|
||||
dictionary[dictionary_ind].prev = prev;
|
||||
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
|
||||
dictionary_ind++;
|
||||
|
||||
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11))
|
||||
{
|
||||
code_length++;
|
||||
dictionary.resize(1 << (code_length + 1));
|
||||
}
|
||||
}
|
||||
|
||||
prev = code;
|
||||
|
||||
// Verifica que 'code' sea un índice válido antes de usarlo.
|
||||
if (code < 0 || static_cast<size_t>(code) >= dictionary.size())
|
||||
{
|
||||
std::cerr << "Invalid LZW code " << code
|
||||
<< ", dictionary size " << dictionary.size() << std::endl;
|
||||
throw std::runtime_error("LZW error: invalid code encountered");
|
||||
}
|
||||
|
||||
int curCode = code; // Variable temporal para recorrer la cadena.
|
||||
match_len = dictionary[curCode].len;
|
||||
while (curCode != -1)
|
||||
{
|
||||
// Se asume que dictionary[curCode].len > 0.
|
||||
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
|
||||
if (dictionary[curCode].prev == curCode)
|
||||
{
|
||||
std::cerr << "Internal error; self-reference detected." << std::endl;
|
||||
throw std::runtime_error("Internal error in decompress: self-reference");
|
||||
}
|
||||
curCode = dictionary[curCode].prev;
|
||||
}
|
||||
out += match_len;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t *&buffer)
|
||||
{
|
||||
std::vector<uint8_t> data;
|
||||
uint8_t block_size = *buffer;
|
||||
buffer++;
|
||||
while (block_size != 0)
|
||||
{
|
||||
data.insert(data.end(), buffer, buffer + block_size);
|
||||
buffer += block_size;
|
||||
block_size = *buffer;
|
||||
buffer++;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits)
|
||||
{
|
||||
ImageDescriptor image_descriptor;
|
||||
// Lee 9 bytes para el image descriptor.
|
||||
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
|
||||
|
||||
uint8_t lzw_code_size;
|
||||
readBytes(buffer, &lzw_code_size, sizeof(uint8_t));
|
||||
|
||||
std::vector<uint8_t> compressed_data = readSubBlocks(buffer);
|
||||
int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height;
|
||||
std::vector<uint8_t> uncompressed_data(uncompressed_data_length);
|
||||
|
||||
decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data());
|
||||
return uncompressed_data;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer)
|
||||
{
|
||||
uint8_t header[6];
|
||||
std::memcpy(header, buffer, 6);
|
||||
buffer += 6;
|
||||
|
||||
ScreenDescriptor screen_descriptor;
|
||||
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
|
||||
buffer += sizeof(ScreenDescriptor);
|
||||
|
||||
std::vector<uint32_t> global_color_table;
|
||||
if (screen_descriptor.fields & 0x80)
|
||||
{
|
||||
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
|
||||
global_color_table.resize(global_color_table_size);
|
||||
for (int i = 0; i < global_color_table_size; ++i)
|
||||
{
|
||||
uint8_t r = buffer[0];
|
||||
uint8_t g = buffer[1];
|
||||
uint8_t b = buffer[2];
|
||||
global_color_table[i] = (r << 16) | (g << 8) | b;
|
||||
buffer += 3;
|
||||
}
|
||||
}
|
||||
return global_color_table;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Gif::processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h)
|
||||
{
|
||||
// Leer la cabecera de 6 bytes ("GIF87a" o "GIF89a")
|
||||
uint8_t header[6];
|
||||
std::memcpy(header, buffer, 6);
|
||||
buffer += 6;
|
||||
|
||||
// Opcional: Validar header
|
||||
std::string headerStr(reinterpret_cast<char *>(header), 6);
|
||||
if (headerStr != "GIF87a" && headerStr != "GIF89a")
|
||||
{
|
||||
throw std::runtime_error("Formato de archivo GIF inválido.");
|
||||
}
|
||||
|
||||
// Leer el Screen Descriptor (7 bytes, empaquetado sin padding)
|
||||
ScreenDescriptor screen_descriptor;
|
||||
readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor));
|
||||
|
||||
// Asigna ancho y alto
|
||||
w = screen_descriptor.width;
|
||||
h = screen_descriptor.height;
|
||||
|
||||
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
|
||||
std::vector<RGB> global_color_table;
|
||||
if (screen_descriptor.fields & 0x80)
|
||||
{
|
||||
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
|
||||
global_color_table.resize(global_color_table_size);
|
||||
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size);
|
||||
buffer += 3 * global_color_table_size;
|
||||
}
|
||||
|
||||
// Supongamos que 'buffer' es el puntero actual y TRAILER es 0x3B
|
||||
uint8_t block_type = *buffer++;
|
||||
while (block_type != TRAILER)
|
||||
{
|
||||
if (block_type == EXTENSION_INTRODUCER) // 0x21
|
||||
{
|
||||
// Se lee la etiqueta de extensión, la cual indica el tipo de extensión.
|
||||
uint8_t extension_label = *buffer++;
|
||||
switch (extension_label)
|
||||
{
|
||||
case GRAPHIC_CONTROL: // 0xF9
|
||||
{
|
||||
// Procesar Graphic Control Extension:
|
||||
uint8_t blockSize = *buffer++; // Normalmente, blockSize == 4
|
||||
buffer += blockSize; // Saltamos los 4 bytes del bloque fijo
|
||||
// Saltar los sub-bloques
|
||||
uint8_t subBlockSize = *buffer++;
|
||||
while (subBlockSize != 0)
|
||||
{
|
||||
buffer += subBlockSize;
|
||||
subBlockSize = *buffer++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APPLICATION_EXTENSION: // 0xFF
|
||||
case COMMENT_EXTENSION: // 0xFE
|
||||
case PLAINTEXT_EXTENSION: // 0x01
|
||||
{
|
||||
// Para estas extensiones, saltamos el bloque fijo y los sub-bloques.
|
||||
uint8_t blockSize = *buffer++;
|
||||
buffer += blockSize;
|
||||
uint8_t subBlockSize = *buffer++;
|
||||
while (subBlockSize != 0)
|
||||
{
|
||||
buffer += subBlockSize;
|
||||
subBlockSize = *buffer++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Si la etiqueta de extensión es desconocida, saltarla también:
|
||||
uint8_t blockSize = *buffer++;
|
||||
buffer += blockSize;
|
||||
uint8_t subBlockSize = *buffer++;
|
||||
while (subBlockSize != 0)
|
||||
{
|
||||
buffer += subBlockSize;
|
||||
subBlockSize = *buffer++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (block_type == IMAGE_DESCRIPTOR)
|
||||
{
|
||||
// Procesar el Image Descriptor y retornar los datos de imagen
|
||||
return processImageDescriptor(buffer, global_color_table, color_resolution_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Unrecognized block type " << std::hex << static_cast<int>(block_type) << std::endl;
|
||||
return std::vector<uint8_t>{};
|
||||
}
|
||||
block_type = *buffer++;
|
||||
}
|
||||
|
||||
return std::vector<uint8_t>{};
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h)
|
||||
{
|
||||
return processGifStream(buffer, w, h);
|
||||
}
|
||||
|
||||
} // namespace GIF
|
||||
102
source/gif.h
Normal file
102
source/gif.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // Para uint8_t, uint16_t, uint32_t
|
||||
#include <vector> // Para vector
|
||||
|
||||
namespace GIF
|
||||
{
|
||||
|
||||
// Constantes definidas con constexpr, en lugar de macros
|
||||
constexpr uint8_t EXTENSION_INTRODUCER = 0x21;
|
||||
constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C;
|
||||
constexpr uint8_t TRAILER = 0x3B;
|
||||
constexpr uint8_t GRAPHIC_CONTROL = 0xF9;
|
||||
constexpr uint8_t APPLICATION_EXTENSION = 0xFF;
|
||||
constexpr uint8_t COMMENT_EXTENSION = 0xFE;
|
||||
constexpr uint8_t PLAINTEXT_EXTENSION = 0x01;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ScreenDescriptor
|
||||
{
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint8_t fields;
|
||||
uint8_t background_color_index;
|
||||
uint8_t pixel_aspect_ratio;
|
||||
};
|
||||
|
||||
struct RGB
|
||||
{
|
||||
uint8_t r, g, b;
|
||||
};
|
||||
|
||||
struct ImageDescriptor
|
||||
{
|
||||
uint16_t image_left_position;
|
||||
uint16_t image_top_position;
|
||||
uint16_t image_width;
|
||||
uint16_t image_height;
|
||||
uint8_t fields;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct DictionaryEntry
|
||||
{
|
||||
uint8_t byte;
|
||||
int prev;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct Extension
|
||||
{
|
||||
uint8_t extension_code;
|
||||
uint8_t block_size;
|
||||
};
|
||||
|
||||
struct GraphicControlExtension
|
||||
{
|
||||
uint8_t fields;
|
||||
uint16_t delay_time;
|
||||
uint8_t transparent_color_index;
|
||||
};
|
||||
|
||||
struct ApplicationExtension
|
||||
{
|
||||
uint8_t application_id[8];
|
||||
uint8_t version[3];
|
||||
};
|
||||
|
||||
struct PlaintextExtension
|
||||
{
|
||||
uint16_t left, top, width, height;
|
||||
uint8_t cell_width, cell_height;
|
||||
uint8_t foreground_color, background_color;
|
||||
};
|
||||
|
||||
class Gif
|
||||
{
|
||||
public:
|
||||
// Descompone (uncompress) el bloque comprimido usando LZW.
|
||||
// Este método puede lanzar std::runtime_error en caso de error.
|
||||
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);
|
||||
|
||||
// Carga la paleta (global color table) a partir de un buffer,
|
||||
// retornándola en un vector de uint32_t (cada color se compone de R, G, B).
|
||||
std::vector<uint32_t> loadPalette(const uint8_t *buffer);
|
||||
|
||||
// Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
|
||||
// asigna el ancho y alto mediante referencias.
|
||||
std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h);
|
||||
|
||||
private:
|
||||
// Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
|
||||
std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);
|
||||
|
||||
// Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
|
||||
std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);
|
||||
|
||||
// Procesa el stream completo del GIF y devuelve los datos sin comprimir.
|
||||
std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
|
||||
};
|
||||
|
||||
} // namespace GIF
|
||||
544
source/jail_audio.cpp
Normal file
544
source/jail_audio.cpp
Normal file
@@ -0,0 +1,544 @@
|
||||
#ifndef JA_USESDLMIXER
|
||||
#include "jail_audio.h"
|
||||
#include <SDL3/SDL_iostream.h> // Para SDL_IOFromMem
|
||||
#include <SDL3/SDL_log.h> // Para SDL_Log, SDL_SetLogPriority, SDL_LogC...
|
||||
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks, SDL_AddTimer, SDL_Remov...
|
||||
#include <stdint.h> // Para uint32_t, uint8_t
|
||||
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen
|
||||
#include <stdlib.h> // Para free, malloc
|
||||
#include "stb_vorbis.c" // Para stb_vorbis_decode_memory
|
||||
|
||||
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
|
||||
|
||||
struct JA_Sound_t
|
||||
{
|
||||
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
|
||||
Uint32 length{0};
|
||||
Uint8 *buffer{NULL};
|
||||
};
|
||||
|
||||
struct JA_Channel_t
|
||||
{
|
||||
JA_Sound_t *sound{nullptr};
|
||||
int pos{0};
|
||||
int times{0};
|
||||
SDL_AudioStream *stream{nullptr};
|
||||
JA_Channel_state state{JA_CHANNEL_FREE};
|
||||
};
|
||||
|
||||
struct JA_Music_t
|
||||
{
|
||||
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
|
||||
Uint32 length{0};
|
||||
Uint8 *buffer{nullptr};
|
||||
|
||||
int pos{0};
|
||||
int times{0};
|
||||
SDL_AudioStream *stream{nullptr};
|
||||
JA_Music_state state{JA_MUSIC_INVALID};
|
||||
};
|
||||
|
||||
JA_Music_t *current_music{nullptr};
|
||||
JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
|
||||
|
||||
SDL_AudioSpec JA_audioSpec{SDL_AUDIO_S16, 2, 48000};
|
||||
float JA_musicVolume{1.0f};
|
||||
float JA_soundVolume{0.5f};
|
||||
bool JA_musicEnabled{true};
|
||||
bool JA_soundEnabled{true};
|
||||
SDL_AudioDeviceID sdlAudioDevice{0};
|
||||
SDL_TimerID JA_timerID{0};
|
||||
|
||||
bool fading = false;
|
||||
int fade_start_time;
|
||||
int fade_duration;
|
||||
int fade_initial_volume;
|
||||
|
||||
/*
|
||||
void audioCallback(void * userdata, uint8_t * stream, int len) {
|
||||
SDL_memset(stream, 0, len);
|
||||
if (current_music != NULL && current_music->state == JA_MUSIC_PLAYING) {
|
||||
const int size = SDL_min(len, current_music->samples*2-current_music->pos);
|
||||
SDL_MixAudioFormat(stream, (Uint8*)(current_music->output+current_music->pos), AUDIO_S16, size, JA_musicVolume);
|
||||
current_music->pos += size/2;
|
||||
if (size < len) {
|
||||
if (current_music->times != 0) {
|
||||
SDL_MixAudioFormat(stream+size, (Uint8*)current_music->output, AUDIO_S16, len-size, JA_musicVolume);
|
||||
current_music->pos = (len-size)/2;
|
||||
if (current_music->times > 0) current_music->times--;
|
||||
} else {
|
||||
current_music->pos = 0;
|
||||
current_music->state = JA_MUSIC_STOPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mixar els channels mi amol
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
|
||||
if (channels[i].state == JA_CHANNEL_PLAYING) {
|
||||
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);
|
||||
channels[i].pos += size;
|
||||
if (size < len) {
|
||||
if (channels[i].times != 0) {
|
||||
SDL_MixAudioFormat(stream + size, channels[i].sound->buffer, AUDIO_S16, len-size, JA_soundVolume);
|
||||
channels[i].pos = len-size;
|
||||
if (channels[i].times > 0) channels[i].times--;
|
||||
} else {
|
||||
JA_StopChannel(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval)
|
||||
{
|
||||
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING)
|
||||
{
|
||||
if (fading)
|
||||
{
|
||||
int time = SDL_GetTicks();
|
||||
if (time > (fade_start_time + fade_duration))
|
||||
{
|
||||
fading = false;
|
||||
JA_StopMusic();
|
||||
return 30;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int time_passed = time - fade_start_time;
|
||||
const float percent = (float)time_passed / (float)fade_duration;
|
||||
SDL_SetAudioStreamGain(current_music->stream, 1.0 - percent);
|
||||
}
|
||||
}
|
||||
|
||||
if (current_music->times != 0)
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(current_music->stream) < static_cast<int>(current_music->length / 2))
|
||||
{
|
||||
SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length);
|
||||
}
|
||||
if (current_music->times > 0)
|
||||
current_music->times--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(current_music->stream) == 0)
|
||||
JA_StopMusic();
|
||||
}
|
||||
}
|
||||
|
||||
if (JA_soundEnabled)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
|
||||
if (channels[i].state == JA_CHANNEL_PLAYING)
|
||||
{
|
||||
if (channels[i].times != 0)
|
||||
{
|
||||
if (SDL_GetAudioStreamAvailable(channels[i].stream) < static_cast<int>(channels[i].sound->length / 2))
|
||||
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
|
||||
if (channels[i].times > 0)
|
||||
channels[i].times--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (channels[i].stream && SDL_GetAudioStreamAvailable(channels[i].stream) == 0)
|
||||
{
|
||||
JA_StopChannel(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 30;
|
||||
}
|
||||
|
||||
void JA_Init(const int freq, const SDL_AudioFormat format, const int channels)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
|
||||
#endif
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Iniciant JailAudio...");
|
||||
JA_audioSpec = {format, channels, freq};
|
||||
if (!sdlAudioDevice)
|
||||
SDL_CloseAudioDevice(sdlAudioDevice);
|
||||
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, (sdlAudioDevice == 0) ? "Failed to initialize SDL audio!\n" : "OK!\n");
|
||||
// SDL_PauseAudioDevice(sdlAudioDevice);
|
||||
JA_timerID = SDL_AddTimer(30, JA_UpdateCallback, nullptr);
|
||||
}
|
||||
|
||||
void JA_Quit()
|
||||
{
|
||||
if (JA_timerID)
|
||||
SDL_RemoveTimer(JA_timerID);
|
||||
|
||||
if (!sdlAudioDevice)
|
||||
SDL_CloseAudioDevice(sdlAudioDevice);
|
||||
sdlAudioDevice = 0;
|
||||
}
|
||||
|
||||
JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length)
|
||||
{
|
||||
JA_Music_t *music = new JA_Music_t();
|
||||
|
||||
int chan, samplerate;
|
||||
short *output;
|
||||
music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2;
|
||||
|
||||
music->spec.channels = chan;
|
||||
music->spec.freq = samplerate;
|
||||
music->spec.format = SDL_AUDIO_S16;
|
||||
music->buffer = (Uint8 *)SDL_malloc(music->length);
|
||||
SDL_memcpy(music->buffer, output, music->length);
|
||||
free(output);
|
||||
music->pos = 0;
|
||||
music->state = JA_MUSIC_STOPPED;
|
||||
|
||||
return music;
|
||||
}
|
||||
|
||||
JA_Music_t *JA_LoadMusic(const char *filename)
|
||||
{
|
||||
// [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid.
|
||||
FILE *f = fopen(filename, "rb");
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
Uint8 *buffer = (Uint8 *)malloc(fsize + 1);
|
||||
if (fread(buffer, fsize, 1, f) != 1)
|
||||
return NULL;
|
||||
fclose(f);
|
||||
|
||||
JA_Music_t *music = JA_LoadMusic(buffer, fsize);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return music;
|
||||
}
|
||||
|
||||
void JA_PlayMusic(JA_Music_t *music, const int loop)
|
||||
{
|
||||
if (!JA_musicEnabled)
|
||||
return;
|
||||
|
||||
JA_StopMusic();
|
||||
|
||||
current_music = music;
|
||||
current_music->pos = 0;
|
||||
current_music->state = JA_MUSIC_PLAYING;
|
||||
current_music->times = loop;
|
||||
|
||||
current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec);
|
||||
if (!SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length))
|
||||
printf("[ERROR] SDL_PutAudioStreamData failed!\n");
|
||||
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
||||
if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream))
|
||||
printf("[ERROR] SDL_BindAudioStream failed!\n");
|
||||
}
|
||||
|
||||
void JA_PauseMusic()
|
||||
{
|
||||
if (!JA_musicEnabled)
|
||||
return;
|
||||
if (!current_music || current_music->state == JA_MUSIC_INVALID)
|
||||
return;
|
||||
|
||||
current_music->state = JA_MUSIC_PAUSED;
|
||||
// SDL_PauseAudioStreamDevice(current_music->stream);
|
||||
SDL_UnbindAudioStream(current_music->stream);
|
||||
}
|
||||
|
||||
void JA_ResumeMusic()
|
||||
{
|
||||
if (!JA_musicEnabled)
|
||||
return;
|
||||
if (!current_music || current_music->state == JA_MUSIC_INVALID)
|
||||
return;
|
||||
|
||||
current_music->state = JA_MUSIC_PLAYING;
|
||||
// SDL_ResumeAudioStreamDevice(current_music->stream);
|
||||
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
|
||||
}
|
||||
|
||||
void JA_StopMusic()
|
||||
{
|
||||
if (!JA_musicEnabled)
|
||||
return;
|
||||
if (!current_music || current_music->state == JA_MUSIC_INVALID)
|
||||
return;
|
||||
|
||||
current_music->pos = 0;
|
||||
current_music->state = JA_MUSIC_STOPPED;
|
||||
// SDL_PauseAudioStreamDevice(current_music->stream);
|
||||
SDL_DestroyAudioStream(current_music->stream);
|
||||
current_music->stream = nullptr;
|
||||
}
|
||||
|
||||
void JA_FadeOutMusic(const int milliseconds)
|
||||
{
|
||||
if (!JA_musicEnabled)
|
||||
return;
|
||||
if (current_music == NULL || current_music->state == JA_MUSIC_INVALID)
|
||||
return;
|
||||
|
||||
fading = true;
|
||||
fade_start_time = SDL_GetTicks();
|
||||
fade_duration = milliseconds;
|
||||
fade_initial_volume = JA_musicVolume;
|
||||
}
|
||||
|
||||
JA_Music_state JA_GetMusicState()
|
||||
{
|
||||
if (!JA_musicEnabled)
|
||||
return JA_MUSIC_DISABLED;
|
||||
if (!current_music)
|
||||
return JA_MUSIC_INVALID;
|
||||
|
||||
return current_music->state;
|
||||
}
|
||||
|
||||
void JA_DeleteMusic(JA_Music_t *music)
|
||||
{
|
||||
if (current_music == music)
|
||||
current_music = nullptr;
|
||||
SDL_free(music->buffer);
|
||||
if (music->stream)
|
||||
SDL_DestroyAudioStream(music->stream);
|
||||
delete music;
|
||||
}
|
||||
|
||||
float JA_SetMusicVolume(float volume)
|
||||
{
|
||||
JA_musicVolume = SDL_clamp(volume, 0.0f, 1.0f);
|
||||
if (current_music)
|
||||
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
|
||||
return JA_musicVolume;
|
||||
}
|
||||
|
||||
void JA_SetMusicPosition(float value)
|
||||
{
|
||||
if (!current_music)
|
||||
return;
|
||||
current_music->pos = value * current_music->spec.freq;
|
||||
}
|
||||
|
||||
float JA_GetMusicPosition()
|
||||
{
|
||||
if (!current_music)
|
||||
return 0;
|
||||
return float(current_music->pos) / float(current_music->spec.freq);
|
||||
}
|
||||
|
||||
void JA_EnableMusic(const bool value)
|
||||
{
|
||||
if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING))
|
||||
JA_StopMusic();
|
||||
|
||||
JA_musicEnabled = value;
|
||||
}
|
||||
|
||||
JA_Sound_t *JA_NewSound(Uint8 *buffer, Uint32 length)
|
||||
{
|
||||
JA_Sound_t *sound = new JA_Sound_t();
|
||||
sound->buffer = buffer;
|
||||
sound->length = length;
|
||||
return sound;
|
||||
}
|
||||
|
||||
JA_Sound_t *JA_LoadSound(uint8_t *buffer, uint32_t size)
|
||||
{
|
||||
JA_Sound_t *sound = new JA_Sound_t();
|
||||
SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &sound->buffer, &sound->length);
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
JA_Sound_t *JA_LoadSound(const char *filename)
|
||||
{
|
||||
JA_Sound_t *sound = new JA_Sound_t();
|
||||
SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length);
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
int JA_PlaySound(JA_Sound_t *sound, const int loop)
|
||||
{
|
||||
if (!JA_soundEnabled)
|
||||
return -1;
|
||||
|
||||
int channel = 0;
|
||||
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE)
|
||||
{
|
||||
channel++;
|
||||
}
|
||||
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
channel = 0;
|
||||
JA_StopChannel(channel);
|
||||
|
||||
channels[channel].sound = sound;
|
||||
channels[channel].times = loop;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
|
||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
||||
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop)
|
||||
{
|
||||
if (!JA_soundEnabled)
|
||||
return -1;
|
||||
|
||||
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
return -1;
|
||||
JA_StopChannel(channel);
|
||||
|
||||
channels[channel].sound = sound;
|
||||
channels[channel].times = loop;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
|
||||
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
|
||||
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
void JA_DeleteSound(JA_Sound_t *sound)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
{
|
||||
if (channels[i].sound == sound)
|
||||
JA_StopChannel(i);
|
||||
}
|
||||
SDL_free(sound->buffer);
|
||||
delete sound;
|
||||
}
|
||||
|
||||
void JA_PauseChannel(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled)
|
||||
return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
if (channels[i].state == JA_CHANNEL_PLAYING)
|
||||
{
|
||||
channels[i].state = JA_CHANNEL_PAUSED;
|
||||
// SDL_PauseAudioStreamDevice(channels[i].stream);
|
||||
SDL_UnbindAudioStream(channels[i].stream);
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state == JA_CHANNEL_PLAYING)
|
||||
{
|
||||
channels[channel].state = JA_CHANNEL_PAUSED;
|
||||
// SDL_PauseAudioStreamDevice(channels[channel].stream);
|
||||
SDL_UnbindAudioStream(channels[channel].stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JA_ResumeChannel(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled)
|
||||
return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
if (channels[i].state == JA_CHANNEL_PAUSED)
|
||||
{
|
||||
channels[i].state = JA_CHANNEL_PLAYING;
|
||||
// SDL_ResumeAudioStreamDevice(channels[i].stream);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[i].stream);
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state == JA_CHANNEL_PAUSED)
|
||||
{
|
||||
channels[channel].state = JA_CHANNEL_PLAYING;
|
||||
// SDL_ResumeAudioStreamDevice(channels[channel].stream);
|
||||
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JA_StopChannel(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled)
|
||||
return;
|
||||
|
||||
if (channel == -1)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
{
|
||||
if (channels[i].state != JA_CHANNEL_FREE)
|
||||
SDL_DestroyAudioStream(channels[i].stream);
|
||||
channels[i].stream = nullptr;
|
||||
channels[i].state = JA_CHANNEL_FREE;
|
||||
channels[i].pos = 0;
|
||||
channels[i].sound = NULL;
|
||||
}
|
||||
}
|
||||
else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
{
|
||||
if (channels[channel].state != JA_CHANNEL_FREE)
|
||||
SDL_DestroyAudioStream(channels[channel].stream);
|
||||
channels[channel].stream = nullptr;
|
||||
channels[channel].state = JA_CHANNEL_FREE;
|
||||
channels[channel].pos = 0;
|
||||
channels[channel].sound = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
JA_Channel_state JA_GetChannelState(const int channel)
|
||||
{
|
||||
if (!JA_soundEnabled)
|
||||
return JA_SOUND_DISABLED;
|
||||
|
||||
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS)
|
||||
return JA_CHANNEL_INVALID;
|
||||
|
||||
return channels[channel].state;
|
||||
}
|
||||
|
||||
float JA_SetSoundVolume(float volume)
|
||||
{
|
||||
JA_soundVolume = SDL_clamp(volume, 0.0f, 1.0f);
|
||||
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
if ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED))
|
||||
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume);
|
||||
|
||||
return JA_soundVolume;
|
||||
}
|
||||
|
||||
void JA_EnableSound(const bool value)
|
||||
{
|
||||
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
|
||||
{
|
||||
if (channels[i].state == JA_CHANNEL_PLAYING)
|
||||
JA_StopChannel(i);
|
||||
}
|
||||
JA_soundEnabled = value;
|
||||
}
|
||||
|
||||
float JA_SetVolume(float volume)
|
||||
{
|
||||
JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f);
|
||||
|
||||
return JA_musicVolume;
|
||||
}
|
||||
|
||||
#endif
|
||||
58
source/jail_audio.h
Normal file
58
source/jail_audio.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_audio.h> // Para SDL_AudioFormat
|
||||
#include <SDL3/SDL_stdinc.h> // Para Uint32, Uint8
|
||||
struct JA_Music_t; // lines 8-8
|
||||
struct JA_Sound_t; // lines 7-7
|
||||
|
||||
enum JA_Channel_state
|
||||
{
|
||||
JA_CHANNEL_INVALID,
|
||||
JA_CHANNEL_FREE,
|
||||
JA_CHANNEL_PLAYING,
|
||||
JA_CHANNEL_PAUSED,
|
||||
JA_SOUND_DISABLED
|
||||
};
|
||||
enum JA_Music_state
|
||||
{
|
||||
JA_MUSIC_INVALID,
|
||||
JA_MUSIC_PLAYING,
|
||||
JA_MUSIC_PAUSED,
|
||||
JA_MUSIC_STOPPED,
|
||||
JA_MUSIC_DISABLED
|
||||
};
|
||||
|
||||
struct JA_Sound_t;
|
||||
struct JA_Music_t;
|
||||
|
||||
void JA_Init(const int freq, const SDL_AudioFormat format, const int channels);
|
||||
void JA_Quit();
|
||||
|
||||
JA_Music_t *JA_LoadMusic(const char *filename);
|
||||
JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length);
|
||||
void JA_PlayMusic(JA_Music_t *music, const int loop = -1);
|
||||
void JA_PauseMusic();
|
||||
void JA_ResumeMusic();
|
||||
void JA_StopMusic();
|
||||
void JA_FadeOutMusic(const int milliseconds);
|
||||
JA_Music_state JA_GetMusicState();
|
||||
void JA_DeleteMusic(JA_Music_t *music);
|
||||
float JA_SetMusicVolume(float volume);
|
||||
void JA_SetMusicPosition(float value);
|
||||
float JA_GetMusicPosition();
|
||||
void JA_EnableMusic(const bool value);
|
||||
|
||||
JA_Sound_t *JA_NewSound(Uint8 *buffer, Uint32 length);
|
||||
JA_Sound_t *JA_LoadSound(Uint8 *buffer, Uint32 length);
|
||||
JA_Sound_t *JA_LoadSound(const char *filename);
|
||||
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);
|
||||
void JA_PauseChannel(const int channel);
|
||||
void JA_ResumeChannel(const int channel);
|
||||
void JA_StopChannel(const int channel);
|
||||
JA_Channel_state JA_GetChannelState(const int channel);
|
||||
void JA_DeleteSound(JA_Sound_t *sound);
|
||||
float JA_SetSoundVolume(float volume);
|
||||
void JA_EnableSound(const bool value);
|
||||
|
||||
float JA_SetVolume(float volume);
|
||||
75
source/main.cpp
Normal file
75
source/main.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <SDL3/SDL.h>
|
||||
#include <memory>
|
||||
#include "screen.h"
|
||||
#include "mouse.h"
|
||||
#include "surface.h"
|
||||
#include "s_sprite.h"
|
||||
|
||||
bool running = true;
|
||||
Uint64 ticks = 0;
|
||||
std::shared_ptr<Surface> logo_surface = nullptr;
|
||||
std::unique_ptr<SSprite> logo_sprite = nullptr;
|
||||
|
||||
void init()
|
||||
{
|
||||
Screen::get()->init();
|
||||
logo_surface = std::make_shared<Surface>("jailgames.gif");
|
||||
logo_sprite = std::make_unique<SSprite>(logo_surface);
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Logo start");
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
Screen::get()->destroy();
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nBye!");
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void checkEvents()
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_QUIT:
|
||||
running = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Mouse::handleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
if (SDL_GetTicks() - ticks > options.game.speed)
|
||||
{
|
||||
ticks = SDL_GetTicks();
|
||||
}
|
||||
}
|
||||
|
||||
void render()
|
||||
{
|
||||
Screen::get()->start();
|
||||
|
||||
logo_sprite->render();
|
||||
|
||||
Screen::get()->render();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
init();
|
||||
|
||||
while (running)
|
||||
{
|
||||
update();
|
||||
checkEvents();
|
||||
render();
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
33
source/mouse.cpp
Normal file
33
source/mouse.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include "mouse.h"
|
||||
#include <SDL3/SDL_mouse.h> // Para SDL_ShowCursor
|
||||
#include <SDL3/SDL_timer.h> // Para SDL_GetTicks
|
||||
|
||||
namespace Mouse
|
||||
{
|
||||
Uint64 cursor_hide_time = 3000; // Tiempo en milisegundos para ocultar el cursor
|
||||
Uint64 last_mouse_move_time = 0; // Última vez que el ratón se movió
|
||||
bool cursor_visible = true; // Estado del cursor
|
||||
|
||||
void handleEvent(const SDL_Event &event)
|
||||
{
|
||||
if (event.type == SDL_EVENT_MOUSE_MOTION)
|
||||
{
|
||||
last_mouse_move_time = SDL_GetTicks();
|
||||
if (!cursor_visible)
|
||||
{
|
||||
SDL_ShowCursor();
|
||||
cursor_visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateCursorVisibility()
|
||||
{
|
||||
Uint64 current_time = SDL_GetTicks();
|
||||
if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time))
|
||||
{
|
||||
SDL_HideCursor();
|
||||
cursor_visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
14
source/mouse.h
Normal file
14
source/mouse.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_events.h> // Para SDL_Event
|
||||
#include <SDL3/SDL_stdinc.h> // Para Uint32
|
||||
|
||||
namespace Mouse
|
||||
{
|
||||
extern Uint64 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor
|
||||
extern Uint64 last_mouse_move_time; // Última vez que el ratón se movió
|
||||
extern bool cursor_visible; // Estado del cursor
|
||||
|
||||
void handleEvent(const SDL_Event &event);
|
||||
void updateCursorVisibility();
|
||||
}
|
||||
20
source/options.cpp
Normal file
20
source/options.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "options.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <algorithm> // Para find_if
|
||||
#include <cctype> // Para isspace
|
||||
#include <fstream> // Para basic_ostream, operator<<, basic_ofstream
|
||||
#include <functional> // Para function
|
||||
#include <iostream> // Para cout, cerr
|
||||
#include <sstream> // Para basic_istringstream
|
||||
#include <string> // Para char_traits, string, operator<<, hash
|
||||
#include <unordered_map> // Para unordered_map, operator==, _Node_const_i...
|
||||
#include <utility> // Para pair
|
||||
|
||||
// Variables
|
||||
Options options;
|
||||
|
||||
// Crea e inicializa las opciones del programa
|
||||
void initOptions()
|
||||
{
|
||||
options = Options();
|
||||
}
|
||||
208
source/options.h
Normal file
208
source/options.h
Normal file
@@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para Uint32
|
||||
#include <string> // Para string, basic_string
|
||||
#include "utils.h" // Para Color, Palette
|
||||
#include <algorithm>
|
||||
|
||||
// Constantes
|
||||
constexpr int DEFAULT_GAME_WIDTH = 480; // Ancho de la ventana por defecto
|
||||
constexpr int DEFAULT_GAME_HEIGHT = 270; // Alto de la ventana por defecto
|
||||
constexpr Uint64 DEFAUL_LOGO_SPEED = 1000 / 60; // Velocidad a la que se ejecuta el logo
|
||||
constexpr int DEFAULT_WINDOW_ZOOM = 2; // Zoom de la ventana por defecto
|
||||
constexpr int DEFAULT_VIDEO_FULLSCREEN = false; // Modo de pantalla completa por defecto
|
||||
constexpr SDL_ScaleMode DEFAULT_SCALE_MODE = SDL_SCALEMODE_NEAREST; // Modo de pantalla completa por defecto
|
||||
constexpr bool DEFAULT_VIDEO_VERTICAL_SYNC = true; // Vsync activado por defecto
|
||||
constexpr bool DEFAULT_VIDEO_INTEGER_SCALE = true; // Escalado entero activado por defecto
|
||||
constexpr bool DEFAULT_VIDEO_KEEP_ASPECT = true; // Mantener aspecto activado por defecto
|
||||
constexpr int DEFAULT_SOUND_VOLUME = 100; // Volumen por defecto de los efectos de sonido
|
||||
constexpr bool DEFAULT_SOUND_ENABLED = true; // Sonido habilitado por defecto
|
||||
constexpr int DEFAULT_MUSIC_VOLUME = 80; // Volumen por defecto de la musica
|
||||
constexpr bool DEFAULT_MUSIC_ENABLED = true; // Musica habilitada por defecto
|
||||
constexpr int DEFAULT_AUDIO_VOLUME = 100; // Volumen por defecto
|
||||
constexpr bool DEFAULT_AUDIO_ENABLED = true; // Audio por defecto
|
||||
constexpr bool DEFAULT_CONSOLE = false; // Consola desactivada por defecto
|
||||
constexpr const char *DEFAULT_WINDOW_CAPTION = "logo_01"; // Versión por defecto
|
||||
|
||||
// Estructura con opciones de la ventana
|
||||
struct OptionsWindow
|
||||
{
|
||||
std::string caption; // Texto que aparece en la barra de titulo de la ventana
|
||||
int zoom; // Zoom de la ventana
|
||||
int max_zoom; // Máximo tamaño de zoom para la ventana
|
||||
|
||||
// Constructor por defecto
|
||||
OptionsWindow()
|
||||
: caption(DEFAULT_WINDOW_CAPTION),
|
||||
zoom(DEFAULT_WINDOW_ZOOM),
|
||||
max_zoom(DEFAULT_WINDOW_ZOOM) {}
|
||||
|
||||
// Constructor
|
||||
OptionsWindow(std::string caption, int zoom, int max_zoom)
|
||||
: caption(caption),
|
||||
zoom(zoom),
|
||||
max_zoom(max_zoom) {}
|
||||
};
|
||||
|
||||
// Estructura para las opciones de video
|
||||
struct OptionsVideo
|
||||
{
|
||||
bool fullscreen; // Contiene el valor del modo de pantalla completav
|
||||
SDL_ScaleMode scale_mode; // Filtro usado para el escalado de la imagen
|
||||
bool vertical_sync; // Indica si se quiere usar vsync o no
|
||||
bool integer_scale; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa
|
||||
bool keep_aspect; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa
|
||||
std::string info; // Información sobre el modo de video
|
||||
|
||||
// Constructor por defecto
|
||||
OptionsVideo()
|
||||
: fullscreen(DEFAULT_VIDEO_FULLSCREEN),
|
||||
scale_mode(DEFAULT_SCALE_MODE),
|
||||
vertical_sync(DEFAULT_VIDEO_VERTICAL_SYNC),
|
||||
integer_scale(DEFAULT_VIDEO_INTEGER_SCALE),
|
||||
keep_aspect(DEFAULT_VIDEO_KEEP_ASPECT),
|
||||
info(std::string()) {}
|
||||
|
||||
// Constructor
|
||||
OptionsVideo(bool fs, SDL_ScaleMode sm, bool vs, bool is, bool ka, std::string i)
|
||||
: fullscreen(fs),
|
||||
scale_mode(sm),
|
||||
vertical_sync(vs),
|
||||
integer_scale(is),
|
||||
keep_aspect(ka),
|
||||
info(i) {}
|
||||
};
|
||||
|
||||
// Estructura para las opciones de musica
|
||||
struct OptionsMusic
|
||||
{
|
||||
bool enabled; // Indica si la música suena o no
|
||||
int volume; // Volumen al que suena la música (0 a 128 internamente)
|
||||
|
||||
// Constructor por defecto
|
||||
OptionsMusic()
|
||||
: enabled(DEFAULT_MUSIC_ENABLED),
|
||||
volume(convertVolume(DEFAULT_MUSIC_VOLUME)) {} // Usa el método estático para la conversión
|
||||
|
||||
// Constructor con parámetros
|
||||
OptionsMusic(bool e, int v)
|
||||
: enabled(e),
|
||||
volume(convertVolume(v)) {} // Convierte el volumen usando el método estático
|
||||
|
||||
// Método para establecer el volumen
|
||||
void setVolume(int v)
|
||||
{
|
||||
v = std::clamp(v, 0, 100); // Ajusta v al rango [0, 100]
|
||||
volume = convertVolume(v); // Convierte al rango interno
|
||||
}
|
||||
|
||||
// Método estático para convertir de 0-100 a 0-128
|
||||
static int convertVolume(int v)
|
||||
{
|
||||
return (v * 128) / 100;
|
||||
}
|
||||
};
|
||||
|
||||
// Estructura para las opciones de sonido
|
||||
struct OptionsSound
|
||||
{
|
||||
bool enabled; // Indica si los sonidos suenan o no
|
||||
int volume; // Volumen al que suenan los sonidos (0 a 128 internamente)
|
||||
|
||||
// Constructor por defecto
|
||||
OptionsSound()
|
||||
: enabled(DEFAULT_SOUND_ENABLED),
|
||||
volume(convertVolume(DEFAULT_SOUND_VOLUME)) {} // Usa el método estático para la conversión
|
||||
|
||||
// Constructor con parámetros
|
||||
OptionsSound(bool e, int v)
|
||||
: enabled(e),
|
||||
volume(convertVolume(v)) {} // También lo integra aquí
|
||||
|
||||
// Método para establecer el volumen
|
||||
void setVolume(int v)
|
||||
{
|
||||
v = std::clamp(v, 0, 100); // Ajusta v al rango [0, 100]
|
||||
volume = convertVolume(v); // Convierte al rango interno
|
||||
}
|
||||
|
||||
// Método estático para convertir de 0-100 a 0-128
|
||||
static int convertVolume(int v)
|
||||
{
|
||||
return (v * 128) / 100;
|
||||
}
|
||||
};
|
||||
|
||||
// Estructura para las opciones de audio
|
||||
struct OptionsAudio
|
||||
{
|
||||
OptionsMusic music; // Opciones para la música
|
||||
OptionsSound sound; // Opciones para los efectos de sonido
|
||||
bool enabled; // Indica si el audio está activo o no
|
||||
int volume; // Volumen al que suenan el audio
|
||||
|
||||
// Constructor por defecto
|
||||
OptionsAudio()
|
||||
: music(OptionsMusic()),
|
||||
sound(OptionsSound()),
|
||||
enabled(DEFAULT_AUDIO_ENABLED),
|
||||
volume(DEFAULT_AUDIO_VOLUME) {}
|
||||
|
||||
// Constructor
|
||||
OptionsAudio(OptionsMusic m, OptionsSound s, bool e, int v)
|
||||
: music(m),
|
||||
sound(s),
|
||||
enabled(e),
|
||||
volume(v) {}
|
||||
};
|
||||
|
||||
// Estructura para las opciones del logo
|
||||
struct OptionsLogo
|
||||
{
|
||||
int width; // Ancho de la resolucion del logo
|
||||
int height; // Alto de la resolucion del logo
|
||||
Uint64 speed; // Velocidad de ejecución del logo
|
||||
|
||||
// Constructor por defecto
|
||||
OptionsLogo()
|
||||
: width(DEFAULT_GAME_WIDTH),
|
||||
height(DEFAULT_GAME_HEIGHT),
|
||||
speed(DEFAUL_LOGO_SPEED) {}
|
||||
|
||||
// Constructor
|
||||
OptionsLogo(int w, int h, Uint64 s)
|
||||
: width(w),
|
||||
height(h),
|
||||
speed(s) {}
|
||||
};
|
||||
|
||||
// Estructura con todas las opciones de configuración del programa
|
||||
struct Options
|
||||
{
|
||||
bool console; // Indica si ha de mostrar información por la consola de texto
|
||||
OptionsLogo game; // Opciones de juego
|
||||
OptionsVideo video; // Opciones de video
|
||||
OptionsWindow window; // Opciones relativas a la ventana
|
||||
OptionsAudio audio; // Opciones relativas al audio
|
||||
|
||||
// Constructor por defecto
|
||||
Options()
|
||||
: console(DEFAULT_CONSOLE),
|
||||
game(OptionsLogo()),
|
||||
video(OptionsVideo()),
|
||||
window(OptionsWindow()),
|
||||
audio(OptionsAudio()) {}
|
||||
|
||||
// Constructor
|
||||
Options(std::string cv, bool c, OptionsLogo g, OptionsVideo v, OptionsWindow sw, OptionsAudio a)
|
||||
: console(c),
|
||||
game(g),
|
||||
video(v),
|
||||
window(sw),
|
||||
audio(a) {}
|
||||
};
|
||||
|
||||
extern Options options;
|
||||
|
||||
// Crea e inicializa las opciones del programa
|
||||
void initOptions();
|
||||
50
source/s_sprite.cpp
Normal file
50
source/s_sprite.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "s_sprite.h"
|
||||
#include "surface.h" // Para Surface
|
||||
|
||||
// Constructor
|
||||
SSprite::SSprite(std::shared_ptr<Surface> surface, int x, int y, int w, int h)
|
||||
: surface_(surface),
|
||||
pos_((SDL_Rect){x, y, w, h}),
|
||||
clip_((SDL_Rect){0, 0, pos_.w, pos_.h}) {}
|
||||
|
||||
SSprite::SSprite(std::shared_ptr<Surface> surface, SDL_Rect rect)
|
||||
: surface_(surface),
|
||||
pos_(rect),
|
||||
clip_((SDL_Rect){0, 0, pos_.w, pos_.h}) {}
|
||||
|
||||
SSprite::SSprite(std::shared_ptr<Surface> surface)
|
||||
: surface_(surface),
|
||||
pos_({0, 0, surface_->getWidth(), surface_->getHeight()}),
|
||||
clip_(pos_) {}
|
||||
|
||||
// Muestra el sprite por pantalla
|
||||
void SSprite::render()
|
||||
{
|
||||
surface_->render(pos_.x, pos_.y, &clip_);
|
||||
}
|
||||
|
||||
void SSprite::render(Uint8 source_color, Uint8 target_color)
|
||||
{
|
||||
surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_);
|
||||
}
|
||||
|
||||
// Establece la posición del objeto
|
||||
void SSprite::setPosition(int x, int y)
|
||||
{
|
||||
pos_.x = x;
|
||||
pos_.y = y;
|
||||
}
|
||||
|
||||
// Establece la posición del objeto
|
||||
void SSprite::setPosition(SDL_Point p)
|
||||
{
|
||||
pos_.x = p.x;
|
||||
pos_.y = p.y;
|
||||
}
|
||||
|
||||
// Reinicia las variables a cero
|
||||
void SSprite::clear()
|
||||
{
|
||||
pos_ = {0, 0, 0, 0};
|
||||
clip_ = {0, 0, 0, 0};
|
||||
}
|
||||
69
source/s_sprite.h
Normal file
69
source/s_sprite.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#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; }
|
||||
};
|
||||
309
source/screen.cpp
Normal file
309
source/screen.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
#include "screen.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <ctype.h> // Para toupper
|
||||
#include <algorithm> // Para max, min, transform
|
||||
#include <fstream> // Para basic_ostream, operator<<, endl, basic_...
|
||||
#include <iostream> // Para cerr
|
||||
#include <iterator> // Para istreambuf_iterator, operator==
|
||||
#include <string> // Para char_traits, string, operator+, operator==
|
||||
#include "mouse.h" // Para updateCursorVisibility
|
||||
#include "options.h" // Para Options, options, OptionsVideo, Border
|
||||
#include "surface.h" // Para Surface, readPalFile
|
||||
|
||||
// [SINGLETON]
|
||||
Screen *Screen::screen_ = nullptr;
|
||||
|
||||
// [SINGLETON] Crearemos el objeto con esta función estática
|
||||
void Screen::init()
|
||||
{
|
||||
Screen::screen_ = new Screen();
|
||||
}
|
||||
|
||||
// [SINGLETON] Destruiremos el objeto con esta función estática
|
||||
void Screen::destroy()
|
||||
{
|
||||
delete Screen::screen_;
|
||||
}
|
||||
|
||||
// [SINGLETON] Con este método obtenemos el objeto y podemos trabajar con él
|
||||
Screen *Screen::get()
|
||||
{
|
||||
return Screen::screen_;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
Screen::Screen()
|
||||
{
|
||||
// Arranca SDL VIDEO, crea la ventana y el renderizador
|
||||
initSDL();
|
||||
|
||||
// Ajusta los tamaños
|
||||
adjustWindowSize();
|
||||
|
||||
// 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);
|
||||
if (!game_texture_)
|
||||
{
|
||||
// Registrar el error si está habilitado
|
||||
if (options.console)
|
||||
{
|
||||
std::cerr << "Error: game_texture_ could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Crea la surface donde se dibujan los graficos del juego
|
||||
game_surface_ = std::make_shared<Surface>(options.game.width, options.game.height);
|
||||
game_surface_->setPalette(readPalFile("jailgames.pal"));
|
||||
game_surface_->clear(0);
|
||||
|
||||
// Establece la surface que actuará como renderer para recibir las llamadas a render()
|
||||
renderer_surface_ = std::make_shared<std::shared_ptr<Surface>>(game_surface_);
|
||||
|
||||
// Establece el modo de video
|
||||
setFullscreenMode(options.video.fullscreen);
|
||||
|
||||
// Muestra la ventana
|
||||
show();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
Screen::~Screen()
|
||||
{
|
||||
SDL_DestroyTexture(game_texture_);
|
||||
SDL_DestroyRenderer(renderer_);
|
||||
SDL_DestroyWindow(window_);
|
||||
}
|
||||
|
||||
// Limpia el renderer
|
||||
void Screen::clearRenderer(Color color)
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF);
|
||||
SDL_RenderClear(renderer_);
|
||||
}
|
||||
|
||||
// Prepara para empezar a dibujar en la textura de juego
|
||||
void Screen::start() { setRendererSurface(nullptr); }
|
||||
|
||||
// Vuelca el contenido del renderizador en pantalla
|
||||
void Screen::render()
|
||||
{
|
||||
// Copia la surface a la textura
|
||||
surfaceToTexture();
|
||||
|
||||
// Copia la textura al renderizador
|
||||
textureToRenderer();
|
||||
}
|
||||
|
||||
// Establece el modo de video
|
||||
void Screen::setFullscreenMode(bool mode)
|
||||
{
|
||||
// Actualiza las opciones
|
||||
options.video.fullscreen = mode;
|
||||
|
||||
// Configura el modo de pantalla
|
||||
SDL_SetWindowFullscreen(window_, options.video.fullscreen);
|
||||
}
|
||||
|
||||
// Camibia entre pantalla completa y ventana
|
||||
void Screen::toggleFullscreen()
|
||||
{
|
||||
options.video.fullscreen = !options.video.fullscreen;
|
||||
setFullscreenMode();
|
||||
}
|
||||
|
||||
// Reduce el tamaño de la ventana
|
||||
bool Screen::decWindowZoom()
|
||||
{
|
||||
if (options.video.fullscreen == 0)
|
||||
{
|
||||
const int PREVIOUS_ZOOM = options.window.zoom;
|
||||
--options.window.zoom;
|
||||
options.window.zoom = std::max(options.window.zoom, 1);
|
||||
|
||||
if (options.window.zoom != PREVIOUS_ZOOM)
|
||||
{
|
||||
setFullscreenMode(options.video.fullscreen);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Aumenta el tamaño de la ventana
|
||||
bool Screen::incWindowZoom()
|
||||
{
|
||||
if (options.video.fullscreen == 0)
|
||||
{
|
||||
const int PREVIOUS_ZOOM = options.window.zoom;
|
||||
++options.window.zoom;
|
||||
options.window.zoom = std::min(options.window.zoom, options.window.max_zoom);
|
||||
|
||||
if (options.window.zoom != PREVIOUS_ZOOM)
|
||||
{
|
||||
setFullscreenMode(options.video.fullscreen);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Actualiza la lógica de la clase
|
||||
void Screen::update()
|
||||
{
|
||||
Mouse::updateCursorVisibility();
|
||||
}
|
||||
|
||||
// Calcula el tamaño de la ventana
|
||||
void Screen::adjustWindowSize()
|
||||
{
|
||||
window_width_ = options.game.width;
|
||||
window_height_ = options.game.height;
|
||||
|
||||
// Establece el nuevo tamaño
|
||||
if (!options.video.fullscreen)
|
||||
{
|
||||
int old_width, old_height;
|
||||
SDL_GetWindowSize(window_, &old_width, &old_height);
|
||||
|
||||
int old_pos_x, old_pos_y;
|
||||
SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y);
|
||||
|
||||
const int NEW_POS_X = old_pos_x + (old_width - (window_width_ * options.window.zoom)) / 2;
|
||||
const int NEW_POS_Y = old_pos_y + (old_height - (window_height_ * options.window.zoom)) / 2;
|
||||
|
||||
SDL_SetWindowSize(window_, window_width_ * options.window.zoom, window_height_ * options.window.zoom);
|
||||
SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// Establece el renderizador para las surfaces
|
||||
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_);
|
||||
}
|
||||
|
||||
// Copia la surface a la textura
|
||||
void Screen::surfaceToTexture()
|
||||
{
|
||||
game_surface_->copyToTexture(renderer_, game_texture_);
|
||||
}
|
||||
|
||||
// Copia la textura al renderizador
|
||||
void Screen::textureToRenderer()
|
||||
{
|
||||
|
||||
SDL_SetRenderTarget(renderer_, nullptr);
|
||||
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
|
||||
SDL_RenderClear(renderer_);
|
||||
SDL_RenderTexture(renderer_, game_texture_, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer_);
|
||||
}
|
||||
|
||||
// Limpia la game_surface_
|
||||
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }
|
||||
|
||||
// Muestra la ventana
|
||||
void Screen::show() { SDL_ShowWindow(window_); }
|
||||
|
||||
// Oculta la ventana
|
||||
void Screen::hide() { SDL_HideWindow(window_); }
|
||||
|
||||
// Getters
|
||||
SDL_Renderer *Screen::getRenderer() { return renderer_; }
|
||||
std::shared_ptr<Surface> Screen::getRendererSurface() { return (*renderer_surface_); }
|
||||
|
||||
// Arranca SDL VIDEO y crea la ventana
|
||||
bool Screen::initSDL()
|
||||
{
|
||||
// Indicador de éxito
|
||||
auto success = true;
|
||||
|
||||
// Inicializa SDL
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_VIDEO could not initialize! SDL Error: %s", SDL_GetError());
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_VIDEO: INITIALIZING\n");
|
||||
|
||||
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"))
|
||||
{
|
||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: opengl not enabled!");
|
||||
}
|
||||
|
||||
// Crea la ventana
|
||||
window_ = SDL_CreateWindow(options.window.caption.c_str(), options.game.width * options.window.zoom, options.game.height * options.window.zoom, SDL_WINDOW_OPENGL);
|
||||
if (!window_)
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Window could not be created! SDL Error: %s", SDL_GetError());
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
||||
|
||||
if (!renderer_)
|
||||
{
|
||||
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Renderer could not be created! SDL Error: %s", SDL_GetError());
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer_, 0x00, 0x00, 0x00, 0xFF);
|
||||
SDL_SetRenderLogicalPresentation(renderer_, options.game.width, options.game.height, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
||||
SDL_SetWindowFullscreen(window_, options.video.fullscreen);
|
||||
SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderVSync(renderer_, options.video.vertical_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "** SDL_VIDEO: INITIALIZATION COMPLETE\n");
|
||||
return success;
|
||||
}
|
||||
|
||||
// Obtiene información sobre la pantalla
|
||||
void Screen::getDisplayInfo()
|
||||
{
|
||||
int i, num_displays = 0;
|
||||
SDL_DisplayID *displays = SDL_GetDisplays(&num_displays);
|
||||
if (displays)
|
||||
{
|
||||
for (i = 0; i < num_displays; ++i)
|
||||
{
|
||||
SDL_DisplayID instance_id = displays[i];
|
||||
const char *name = SDL_GetDisplayName(instance_id);
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Display %" SDL_PRIu32 ": %s", instance_id, name ? name : "Unknown");
|
||||
}
|
||||
|
||||
auto DM = SDL_GetCurrentDisplayMode(displays[0]);
|
||||
|
||||
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
|
||||
options.window.max_zoom = std::min(DM->w / options.game.width, DM->h / options.game.height);
|
||||
options.window.zoom = std::min(options.window.zoom, options.window.max_zoom);
|
||||
|
||||
// Muestra información sobre el tamaño de la pantalla y de la ventana de juego
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz",
|
||||
static_cast<int>(DM->w), static_cast<int>(DM->h), static_cast<int>(DM->refresh_rate));
|
||||
|
||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d",
|
||||
static_cast<int>(options.game.width), static_cast<int>(options.game.height), options.window.zoom);
|
||||
|
||||
options.video.info = std::to_string(static_cast<int>(DM->w)) + " X " +
|
||||
std::to_string(static_cast<int>(DM->h)) + " AT " +
|
||||
std::to_string(static_cast<int>(DM->refresh_rate)) + " HZ";
|
||||
|
||||
// Calcula el máximo factor de zoom que se puede aplicar a la pantalla
|
||||
const int MAX_ZOOM = std::min(DM->w / options.game.width, (DM->h - WINDOWS_DECORATIONS_) / options.game.height);
|
||||
|
||||
// Normaliza los valores de zoom
|
||||
options.window.zoom = std::min(options.window.zoom, MAX_ZOOM);
|
||||
|
||||
SDL_free(displays);
|
||||
}
|
||||
}
|
||||
104
source/screen.h
Normal file
104
source/screen.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stddef.h> // Para size_t
|
||||
#include <memory> // Para shared_ptr, __shared_ptr_access
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
#include "utils.h" // Para Color
|
||||
#include "options.h"
|
||||
struct Surface;
|
||||
|
||||
class Screen
|
||||
{
|
||||
private:
|
||||
// Constantes
|
||||
static constexpr int WINDOWS_DECORATIONS_ = 35;
|
||||
|
||||
// [SINGLETON] Objeto privado
|
||||
static Screen *screen_;
|
||||
|
||||
// Objetos y punteros
|
||||
SDL_Window *window_; // Ventana de la aplicación
|
||||
SDL_Renderer *renderer_; // El renderizador de la ventana
|
||||
|
||||
SDL_Texture *game_texture_; // Textura donde se dibuja el juego
|
||||
|
||||
std::shared_ptr<Surface> game_surface_; // Surface principal para manejar game_surface_data_
|
||||
std::shared_ptr<std::shared_ptr<Surface>> renderer_surface_; // Puntero a la Surface que actua
|
||||
|
||||
// Variables
|
||||
int window_width_; // Ancho de la pantalla o ventana
|
||||
int window_height_; // Alto de la pantalla o ventana
|
||||
|
||||
// Arranca SDL VIDEO y crea la ventana
|
||||
bool initSDL();
|
||||
|
||||
// Calcula el tamaño de la ventana
|
||||
void adjustWindowSize();
|
||||
|
||||
// Copia la surface a la textura
|
||||
void surfaceToTexture();
|
||||
|
||||
// Copia la textura al renderizador
|
||||
void textureToRenderer();
|
||||
|
||||
// Obtiene información sobre la pantalla
|
||||
void getDisplayInfo();
|
||||
|
||||
// Constructor
|
||||
Screen();
|
||||
|
||||
// Destructor
|
||||
~Screen();
|
||||
|
||||
public:
|
||||
// [SINGLETON] Crearemos el objeto con esta función estática
|
||||
static void init();
|
||||
|
||||
// [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 Screen *get();
|
||||
|
||||
// Limpia el renderer
|
||||
void clearRenderer(Color color = {0x00, 0x00, 0x00});
|
||||
|
||||
// Limpia la game_surface_
|
||||
void clearSurface(Uint8 index);
|
||||
|
||||
// Prepara para empezar a dibujar en la textura de juego
|
||||
void start();
|
||||
|
||||
// Vuelca el contenido del renderizador en pantalla
|
||||
void render();
|
||||
|
||||
// Actualiza la lógica de la clase
|
||||
void update();
|
||||
|
||||
// Establece el modo de video
|
||||
void setFullscreenMode(bool mode = options.video.fullscreen);
|
||||
|
||||
// Cambia entre pantalla completa y ventana
|
||||
void toggleFullscreen();
|
||||
|
||||
// Reduce el tamaño de la ventana
|
||||
bool decWindowZoom();
|
||||
|
||||
// Aumenta el tamaño de la ventana
|
||||
bool incWindowZoom();
|
||||
|
||||
// Muestra la ventana
|
||||
void show();
|
||||
|
||||
// Oculta la ventana
|
||||
void hide();
|
||||
|
||||
// Establece el renderizador para las surfaces
|
||||
void setRendererSurface(std::shared_ptr<Surface> surface = nullptr);
|
||||
|
||||
// Getters
|
||||
SDL_Renderer *getRenderer();
|
||||
std::shared_ptr<Surface> getRendererSurface();
|
||||
};
|
||||
9251
source/stb_image.h
Normal file
9251
source/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
5565
source/stb_vorbis.c
Normal file
5565
source/stb_vorbis.c
Normal file
File diff suppressed because it is too large
Load Diff
634
source/surface.cpp
Normal file
634
source/surface.cpp
Normal file
@@ -0,0 +1,634 @@
|
||||
// IWYU pragma: no_include <bits/std_abs.h>
|
||||
#include "surface.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <cmath> // Para abs
|
||||
#include <algorithm> // Para min, max, copy_n, fill
|
||||
#include <cstdint> // Para uint32_t
|
||||
#include <cstring> // Para memcpy, size_t
|
||||
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ist...
|
||||
#include <iostream> // Para cerr
|
||||
#include <memory> // Para shared_ptr, __shared_ptr_access, default...
|
||||
#include <sstream> // Para basic_istringstream
|
||||
#include <stdexcept> // Para runtime_error
|
||||
#include <vector> // Para vector
|
||||
#include "gif.h" // Para Gif
|
||||
#include "screen.h" // Para Screen
|
||||
|
||||
// Carga una paleta desde un archivo .gif
|
||||
Palette loadPalette(const std::string &file_path)
|
||||
{
|
||||
// Abrir el archivo en modo binario
|
||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
{
|
||||
throw std::runtime_error("Error opening file: " + file_path);
|
||||
}
|
||||
|
||||
// Obtener el tamaño del archivo y leerlo en un buffer
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<Uint8> buffer(size);
|
||||
if (!file.read(reinterpret_cast<char *>(buffer.data()), size))
|
||||
{
|
||||
throw std::runtime_error("Error reading file: " + file_path);
|
||||
}
|
||||
|
||||
// Cargar la paleta usando los datos del buffer
|
||||
GIF::Gif gif;
|
||||
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
|
||||
if (pal.empty())
|
||||
{
|
||||
throw std::runtime_error("No palette found in GIF file: " + file_path);
|
||||
}
|
||||
|
||||
// Crear la paleta y copiar los datos desde 'pal'
|
||||
Palette palette = {}; // Inicializa la paleta con ceros
|
||||
std::copy_n(pal.begin(), std::min(pal.size(), palette.size()), palette.begin());
|
||||
|
||||
// Mensaje de depuración
|
||||
printWithDots("Palette : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
// Carga una paleta desde un archivo .pal
|
||||
Palette readPalFile(const std::string &file_path)
|
||||
{
|
||||
Palette palette{};
|
||||
palette.fill(0); // Inicializar todo con 0 (transparente por defecto)
|
||||
|
||||
std::ifstream file(file_path);
|
||||
if (!file.is_open())
|
||||
{
|
||||
throw std::runtime_error("No se pudo abrir el archivo .pal");
|
||||
}
|
||||
|
||||
std::string line;
|
||||
int line_number = 0;
|
||||
int color_index = 0;
|
||||
|
||||
while (std::getline(file, line))
|
||||
{
|
||||
++line_number;
|
||||
|
||||
// Ignorar las tres primeras líneas del archivo
|
||||
if (line_number <= 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Procesar las líneas restantes con valores RGB
|
||||
std::istringstream ss(line);
|
||||
int r, g, b;
|
||||
if (ss >> r >> g >> b)
|
||||
{
|
||||
// Construir el color ARGB (A = 255 por defecto)
|
||||
Uint32 color = (255 << 24) | (r << 16) | (g << 8) | b;
|
||||
palette[color_index++] = color;
|
||||
|
||||
// Limitar a un máximo de 256 colores (opcional)
|
||||
if (color_index >= 256)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return palette;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
Surface::Surface(int w, int h)
|
||||
: surface_data_(std::make_shared<SurfaceData>(w, h)),
|
||||
transparent_color_(255) { initializeSubPalette(sub_palette_); }
|
||||
|
||||
Surface::Surface(const std::string &file_path)
|
||||
: transparent_color_(255)
|
||||
{
|
||||
SurfaceData loadedData = loadSurface(file_path);
|
||||
surface_data_ = std::make_shared<SurfaceData>(std::move(loadedData));
|
||||
|
||||
initializeSubPalette(sub_palette_);
|
||||
}
|
||||
|
||||
// Carga una superficie desde un archivo
|
||||
SurfaceData Surface::loadSurface(const std::string &file_path)
|
||||
{
|
||||
// Abrir el archivo usando std::ifstream para manejo automático del recurso
|
||||
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open())
|
||||
{
|
||||
std::cerr << "Error opening file: " << file_path << std::endl;
|
||||
throw std::runtime_error("Error opening file");
|
||||
}
|
||||
|
||||
// Obtener el tamaño del archivo
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
// Leer el contenido del archivo en un buffer
|
||||
std::vector<Uint8> buffer(size);
|
||||
if (!file.read(reinterpret_cast<char *>(buffer.data()), size))
|
||||
{
|
||||
std::cerr << "Error reading file: " << file_path << std::endl;
|
||||
throw std::runtime_error("Error reading file");
|
||||
}
|
||||
|
||||
// Crear un objeto Gif y llamar a la función loadGif
|
||||
GIF::Gif gif;
|
||||
Uint16 w = 0, h = 0;
|
||||
std::vector<Uint8> rawPixels = gif.loadGif(buffer.data(), w, h);
|
||||
if (rawPixels.empty())
|
||||
{
|
||||
std::cerr << "Error loading GIF from file: " << file_path << std::endl;
|
||||
throw std::runtime_error("Error loading GIF");
|
||||
}
|
||||
|
||||
// Si el constructor de Surface espera un std::shared_ptr<Uint8[]>,
|
||||
// reservamos un bloque dinámico y copiamos los datos del vector.
|
||||
size_t pixelCount = rawPixels.size();
|
||||
auto pixels = std::shared_ptr<Uint8[]>(new Uint8[pixelCount], std::default_delete<Uint8[]>());
|
||||
std::memcpy(pixels.get(), rawPixels.data(), pixelCount);
|
||||
|
||||
// Crear y devolver directamente el objeto SurfaceData
|
||||
printWithDots("Surface : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]");
|
||||
return SurfaceData(w, h, pixels);
|
||||
}
|
||||
|
||||
// Carga una paleta desde un archivo
|
||||
void Surface::loadPalette(const std::string &file_path)
|
||||
{
|
||||
palette_ = ::loadPalette(file_path);
|
||||
}
|
||||
|
||||
// Carga una paleta desde otra paleta
|
||||
void Surface::loadPalette(Palette palette)
|
||||
{
|
||||
palette_ = palette;
|
||||
}
|
||||
|
||||
// Establece un color en la paleta
|
||||
void Surface::setColor(int index, Uint32 color)
|
||||
{
|
||||
palette_.at(index) = color;
|
||||
}
|
||||
|
||||
// Rellena la superficie con un color
|
||||
void Surface::clear(Uint8 color)
|
||||
{
|
||||
const size_t total_pixels = surface_data_->width * surface_data_->height;
|
||||
Uint8 *data_ptr = surface_data_->data.get();
|
||||
std::fill(data_ptr, data_ptr + total_pixels, color);
|
||||
}
|
||||
|
||||
// Pone un pixel en la SurfaceData
|
||||
void Surface::putPixel(int x, int y, Uint8 color)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= surface_data_->width || y >= surface_data_->height)
|
||||
{
|
||||
return; // Coordenadas fuera de rango
|
||||
}
|
||||
|
||||
const int index = x + y * surface_data_->width;
|
||||
surface_data_->data.get()[index] = color;
|
||||
}
|
||||
|
||||
// Obtiene el color de un pixel de la surface_data
|
||||
Uint8 Surface::getPixel(int x, int y) { return surface_data_->data.get()[x + y * surface_data_->width]; }
|
||||
|
||||
// Dibuja un rectangulo relleno
|
||||
void Surface::fillRect(const SDL_Rect *rect, Uint8 color)
|
||||
{
|
||||
// Limitar los valores del rectángulo al tamaño de la superficie
|
||||
int x_start = std::max(0, rect->x);
|
||||
int y_start = std::max(0, rect->y);
|
||||
int x_end = std::min(rect->x + rect->w, static_cast<int>(surface_data_->width));
|
||||
int y_end = std::min(rect->y + rect->h, static_cast<int>(surface_data_->height));
|
||||
|
||||
// Recorrer cada píxel dentro del rectángulo directamente
|
||||
for (int y = y_start; y < y_end; ++y)
|
||||
{
|
||||
for (int x = x_start; x < x_end; ++x)
|
||||
{
|
||||
const int index = x + y * surface_data_->width;
|
||||
surface_data_->data.get()[index] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja el borde de un rectangulo
|
||||
void Surface::drawRectBorder(const SDL_Rect *rect, Uint8 color)
|
||||
{
|
||||
// Limitar los valores del rectángulo al tamaño de la superficie
|
||||
int x_start = std::max(0, rect->x);
|
||||
int y_start = std::max(0, rect->y);
|
||||
int x_end = std::min(rect->x + rect->w, static_cast<int>(surface_data_->width));
|
||||
int y_end = std::min(rect->y + rect->h, static_cast<int>(surface_data_->height));
|
||||
|
||||
// Dibujar bordes horizontales
|
||||
for (int x = x_start; x < x_end; ++x)
|
||||
{
|
||||
// Borde superior
|
||||
const int top_index = x + y_start * surface_data_->width;
|
||||
surface_data_->data.get()[top_index] = color;
|
||||
|
||||
// Borde inferior
|
||||
const int bottom_index = x + (y_end - 1) * surface_data_->width;
|
||||
surface_data_->data.get()[bottom_index] = color;
|
||||
}
|
||||
|
||||
// Dibujar bordes verticales
|
||||
for (int y = y_start; y < y_end; ++y)
|
||||
{
|
||||
// Borde izquierdo
|
||||
const int left_index = x_start + y * surface_data_->width;
|
||||
surface_data_->data.get()[left_index] = color;
|
||||
|
||||
// Borde derecho
|
||||
const int right_index = (x_end - 1) + y * surface_data_->width;
|
||||
surface_data_->data.get()[right_index] = color;
|
||||
}
|
||||
}
|
||||
|
||||
// Dibuja una linea
|
||||
void Surface::drawLine(int x1, int y1, int x2, int y2, Uint8 color)
|
||||
{
|
||||
// Calcula las diferencias
|
||||
int dx = std::abs(x2 - x1);
|
||||
int dy = std::abs(y2 - y1);
|
||||
|
||||
// Determina la dirección del incremento
|
||||
int sx = (x1 < x2) ? 1 : -1;
|
||||
int sy = (y1 < y2) ? 1 : -1;
|
||||
|
||||
int err = dx - dy;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
surface_data_->data.get()[x1 + y1 * surface_data_->width] = color;
|
||||
}
|
||||
|
||||
// Si alcanzamos el punto final, salimos
|
||||
if (x1 == x2 && y1 == y2)
|
||||
break;
|
||||
|
||||
int e2 = 2 * err;
|
||||
if (e2 > -dy)
|
||||
{
|
||||
err -= dy;
|
||||
x1 += sx;
|
||||
}
|
||||
if (e2 < dx)
|
||||
{
|
||||
err += dx;
|
||||
y1 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Surface::render(int dx, int dy, int sx, int sy, int w, int h)
|
||||
{
|
||||
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
|
||||
|
||||
// Limitar la región para evitar accesos fuera de rango en origen
|
||||
w = std::min(w, surface_data_->width - sx);
|
||||
h = std::min(h, surface_data_->height - sy);
|
||||
|
||||
// Limitar la región para evitar accesos fuera de rango en destino
|
||||
w = std::min(w, surface_data->width - dx);
|
||||
h = std::min(h, surface_data->height - dy);
|
||||
|
||||
for (int iy = 0; iy < h; ++iy)
|
||||
{
|
||||
for (int ix = 0; ix < w; ++ix)
|
||||
{
|
||||
// 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_y = dy + iy; dest_y >= 0 && dest_y < surface_data->height)
|
||||
{
|
||||
int src_x = sx + ix;
|
||||
int src_y = sy + iy;
|
||||
|
||||
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
|
||||
if (color != transparent_color_)
|
||||
{
|
||||
surface_data->data.get()[dest_x + dest_y * surface_data->width] = sub_palette_[color];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Surface::render(int x, int y, SDL_Rect *srcRect, SDL_FlipMode flip)
|
||||
{
|
||||
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
|
||||
|
||||
// Determina la región de origen (clip) a renderizar
|
||||
int sx = (srcRect) ? srcRect->x : 0;
|
||||
int sy = (srcRect) ? srcRect->y : 0;
|
||||
int w = (srcRect) ? srcRect->w : surface_data_->width;
|
||||
int h = (srcRect) ? srcRect->h : surface_data_->height;
|
||||
|
||||
// Limitar la región para evitar accesos fuera de rango en origen
|
||||
w = std::min(w, surface_data_->width - sx);
|
||||
h = std::min(h, surface_data_->height - sy);
|
||||
w = std::min(w, surface_data_dest->width - x);
|
||||
h = std::min(h, surface_data_dest->height - y);
|
||||
|
||||
// Limitar la región para evitar accesos fuera de rango en destino
|
||||
w = std::min(w, surface_data_dest->width - x);
|
||||
h = std::min(h, surface_data_dest->height - y);
|
||||
|
||||
// Renderiza píxel por píxel aplicando el flip si es necesario
|
||||
for (int iy = 0; iy < h; ++iy)
|
||||
{
|
||||
for (int ix = 0; ix < w; ++ix)
|
||||
{
|
||||
// Coordenadas de origen
|
||||
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);
|
||||
|
||||
// Coordenadas de destino
|
||||
int dest_x = x + ix;
|
||||
int dest_y = y + iy;
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Copia el píxel si no es transparente
|
||||
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
|
||||
if (color != transparent_color_)
|
||||
{
|
||||
surface_data_dest->data[dest_x + dest_y * surface_data_dest->width] = sub_palette_[color];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copia una región de la superficie de origen a la de destino
|
||||
void Surface::render(SDL_Rect *srcRect, SDL_Rect *dstRect, SDL_FlipMode flip)
|
||||
{
|
||||
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
|
||||
|
||||
// Si srcRect es nullptr, tomar toda la superficie fuente
|
||||
int sx = (srcRect) ? srcRect->x : 0;
|
||||
int sy = (srcRect) ? srcRect->y : 0;
|
||||
int sw = (srcRect) ? srcRect->w : surface_data_->width;
|
||||
int sh = (srcRect) ? srcRect->h : surface_data_->height;
|
||||
|
||||
// Si dstRect es nullptr, asignar las mismas dimensiones que srcRect
|
||||
int dx = (dstRect) ? dstRect->x : 0;
|
||||
int dy = (dstRect) ? dstRect->y : 0;
|
||||
int dw = (dstRect) ? dstRect->w : sw;
|
||||
int dh = (dstRect) ? dstRect->h : sh;
|
||||
|
||||
// Asegurarse de que srcRect y dstRect tienen las mismas dimensiones
|
||||
if (sw != dw || sh != dh)
|
||||
{
|
||||
dw = sw; // Respetar las dimensiones de srcRect
|
||||
dh = sh;
|
||||
}
|
||||
|
||||
// Limitar la región para evitar accesos fuera de rango en src y dst
|
||||
sw = std::min(sw, surface_data_->width - sx);
|
||||
sh = std::min(sh, surface_data_->height - sy);
|
||||
dw = std::min(dw, surface_data->width - dx);
|
||||
dh = std::min(dh, surface_data->height - dy);
|
||||
|
||||
int final_width = std::min(sw, dw);
|
||||
int final_height = std::min(sh, dh);
|
||||
|
||||
// Renderiza píxel por píxel aplicando el flip si es necesario
|
||||
for (int iy = 0; iy < final_height; ++iy)
|
||||
{
|
||||
for (int ix = 0; ix < final_width; ++ix)
|
||||
{
|
||||
// Coordenadas de origen
|
||||
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);
|
||||
|
||||
// Coordenadas de destino
|
||||
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)
|
||||
{
|
||||
// Copiar el píxel si no es transparente
|
||||
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
|
||||
if (color != transparent_color_)
|
||||
{
|
||||
surface_data->data[dest_x + dest_y * surface_data->width] = sub_palette_[color];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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_FlipMode flip)
|
||||
{
|
||||
auto surface_data = Screen::get()->getRendererSurface()->getSurfaceData();
|
||||
|
||||
// Determina la región de origen (clip) a renderizar
|
||||
int sx = (srcRect) ? srcRect->x : 0;
|
||||
int sy = (srcRect) ? srcRect->y : 0;
|
||||
int w = (srcRect) ? srcRect->w : surface_data_->width;
|
||||
int h = (srcRect) ? srcRect->h : surface_data_->height;
|
||||
|
||||
// Limitar la región para evitar accesos fuera de rango
|
||||
w = std::min(w, surface_data_->width - sx);
|
||||
h = std::min(h, surface_data_->height - sy);
|
||||
|
||||
// Renderiza píxel por píxel aplicando el flip si es necesario
|
||||
for (int iy = 0; iy < h; ++iy)
|
||||
{
|
||||
for (int ix = 0; ix < w; ++ix)
|
||||
{
|
||||
// Coordenadas de origen
|
||||
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);
|
||||
|
||||
// Coordenadas de destino
|
||||
int dest_x = x + ix;
|
||||
int dest_y = y + iy;
|
||||
|
||||
// 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)
|
||||
{
|
||||
continue; // Saltar píxeles fuera del rango del destino
|
||||
}
|
||||
|
||||
// Copia el píxel si no es transparente
|
||||
Uint8 color = surface_data_->data.get()[src_x + src_y * surface_data_->width];
|
||||
if (color != transparent_color_)
|
||||
{
|
||||
surface_data->data[dest_x + dest_y * surface_data->width] =
|
||||
(color == source_color) ? target_color : color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vuelca la superficie a una textura
|
||||
void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture)
|
||||
{
|
||||
if (!renderer || !texture || !surface_data_)
|
||||
{
|
||||
throw std::runtime_error("Renderer or texture is null.");
|
||||
}
|
||||
|
||||
if (surface_data_->width <= 0 || surface_data_->height <= 0 || !surface_data_->data.get())
|
||||
{
|
||||
throw std::runtime_error("Invalid surface dimensions or data.");
|
||||
}
|
||||
|
||||
Uint32 *pixels = nullptr;
|
||||
int pitch = 0;
|
||||
|
||||
// Bloquea la textura para modificar los píxeles directamente
|
||||
if (SDL_LockTexture(texture, nullptr, reinterpret_cast<void **>(&pixels), &pitch) != 0)
|
||||
{
|
||||
throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError()));
|
||||
}
|
||||
|
||||
// Convertir `pitch` de bytes a Uint32 (asegurando alineación correcta en hardware)
|
||||
int row_stride = pitch / sizeof(Uint32);
|
||||
|
||||
for (int y = 0; y < surface_data_->height; ++y)
|
||||
{
|
||||
for (int x = 0; x < surface_data_->width; ++x)
|
||||
{
|
||||
// Calcular la posición correcta en la textura teniendo en cuenta el stride
|
||||
int texture_index = y * row_stride + x;
|
||||
int surface_index = y * surface_data_->width + x;
|
||||
|
||||
pixels[texture_index] = palette_[surface_data_->data.get()[surface_index]];
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(texture); // Desbloquea la textura
|
||||
|
||||
// Renderiza la textura en la pantalla completa
|
||||
if (!SDL_RenderTexture(renderer, texture, nullptr, nullptr))
|
||||
{
|
||||
throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
// Vuelca la superficie a una textura
|
||||
void Surface::copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_FRect *srcRect, SDL_FRect *destRect)
|
||||
{
|
||||
if (!renderer || !texture || !surface_data_)
|
||||
{
|
||||
throw std::runtime_error("Renderer or texture is null.");
|
||||
}
|
||||
|
||||
if (surface_data_->width <= 0 || surface_data_->height <= 0 || !surface_data_->data.get())
|
||||
{
|
||||
throw std::runtime_error("Invalid surface dimensions or data.");
|
||||
}
|
||||
|
||||
Uint32 *pixels = nullptr;
|
||||
int pitch = 0;
|
||||
SDL_Rect destSDLRect = {
|
||||
static_cast<int>(destRect->x),
|
||||
static_cast<int>(destRect->y),
|
||||
static_cast<int>(destRect->w),
|
||||
static_cast<int>(destRect->h)
|
||||
};
|
||||
|
||||
if (SDL_LockTexture(texture, &destSDLRect, reinterpret_cast<void **>(&pixels), &pitch) != 0)
|
||||
{
|
||||
throw std::runtime_error("Failed to lock texture: " + std::string(SDL_GetError()));
|
||||
}
|
||||
|
||||
int row_stride = pitch / sizeof(Uint32);
|
||||
|
||||
for (int y = 0; y < surface_data_->height; ++y)
|
||||
{
|
||||
for (int x = 0; x < surface_data_->width; ++x)
|
||||
{
|
||||
int texture_index = y * row_stride + x;
|
||||
int surface_index = y * surface_data_->width + x;
|
||||
|
||||
pixels[texture_index] = palette_[surface_data_->data.get()[surface_index]];
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(texture);
|
||||
|
||||
// Renderiza la textura con los rectángulos especificados
|
||||
if (!SDL_RenderTexture(renderer, texture, srcRect, destRect))
|
||||
{
|
||||
throw std::runtime_error("Failed to copy texture to renderer: " + std::string(SDL_GetError()));
|
||||
}
|
||||
}
|
||||
|
||||
// Realiza un efecto de fundido en la paleta principal
|
||||
bool Surface::fadePalette()
|
||||
{
|
||||
// Verificar que el tamaño mínimo de palette_ sea adecuado
|
||||
static constexpr int palette_size = 19;
|
||||
if (sizeof(palette_) / sizeof(palette_[0]) < palette_size)
|
||||
{
|
||||
throw std::runtime_error("Palette size is insufficient for fadePalette operation.");
|
||||
}
|
||||
|
||||
// Desplazar colores (pares e impares)
|
||||
for (int i = 18; i > 1; --i)
|
||||
{
|
||||
palette_[i] = palette_[i - 2];
|
||||
}
|
||||
|
||||
// Ajustar el primer color
|
||||
palette_[1] = palette_[0];
|
||||
|
||||
// Devolver si el índice 15 coincide con el índice 0
|
||||
return palette_[15] == palette_[0];
|
||||
}
|
||||
|
||||
// Realiza un efecto de fundido en la paleta secundaria
|
||||
bool Surface::fadeSubPalette(Uint32 delay)
|
||||
{
|
||||
// Variable estática para almacenar el último tick
|
||||
static Uint32 last_tick = 0;
|
||||
|
||||
// Obtener el tiempo actual
|
||||
Uint32 current_tick = SDL_GetTicks();
|
||||
|
||||
// Verificar si ha pasado el tiempo de retardo
|
||||
if (current_tick - last_tick < delay)
|
||||
{
|
||||
return false; // No se realiza el fade
|
||||
}
|
||||
|
||||
// Actualizar el último tick
|
||||
last_tick = current_tick;
|
||||
|
||||
// Verificar que el tamaño mínimo de sub_palette_ sea adecuado
|
||||
static constexpr int sub_palette_size = 19;
|
||||
if (sizeof(sub_palette_) / sizeof(sub_palette_[0]) < sub_palette_size)
|
||||
{
|
||||
throw std::runtime_error("Palette size is insufficient for fadePalette operation.");
|
||||
}
|
||||
|
||||
// Desplazar colores (pares e impares)
|
||||
for (int i = 18; i > 1; --i)
|
||||
{
|
||||
sub_palette_[i] = sub_palette_[i - 2];
|
||||
}
|
||||
|
||||
// Ajustar el primer color
|
||||
sub_palette_[1] = sub_palette_[0];
|
||||
|
||||
// Devolver si el índice 15 coincide con el índice 0
|
||||
return sub_palette_[15] == sub_palette_[0];
|
||||
}
|
||||
126
source/surface.h
Normal file
126
source/surface.h
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <array> // Para array
|
||||
#include <memory> // Para default_delete, shared_ptr, __shared_pt...
|
||||
#include <numeric> // Para iota
|
||||
#include <string> // Para string
|
||||
#include <utility> // Para move
|
||||
#include "utils.h" // Para PaletteColor
|
||||
|
||||
// Alias
|
||||
using Palette = std::array<Uint32, 256>;
|
||||
using SubPalette = std::array<Uint8, 256>;
|
||||
|
||||
// Carga una paleta desde un archivo .gif
|
||||
Palette loadPalette(const std::string &file_path);
|
||||
|
||||
// Carga una paleta desde un archivo .pal
|
||||
Palette readPalFile(const std::string &file_path);
|
||||
|
||||
struct SurfaceData
|
||||
{
|
||||
std::shared_ptr<Uint8[]> data; // Usa std::shared_ptr para gestión automática
|
||||
Uint16 width; // Ancho de la imagen
|
||||
Uint16 height; // Alto de la imagen
|
||||
|
||||
// Constructor por defecto
|
||||
SurfaceData() : data(nullptr), width(0), height(0) {}
|
||||
|
||||
// Constructor que inicializa dimensiones y asigna memoria
|
||||
SurfaceData(Uint16 w, Uint16 h)
|
||||
: data(std::shared_ptr<Uint8[]>(new Uint8[w * h](), std::default_delete<Uint8[]>())), width(w), height(h) {}
|
||||
|
||||
// Constructor para inicializar directamente con datos
|
||||
SurfaceData(Uint16 w, Uint16 h, std::shared_ptr<Uint8[]> pixels)
|
||||
: data(std::move(pixels)), width(w), height(h) {}
|
||||
|
||||
// Constructor de movimiento
|
||||
SurfaceData(SurfaceData &&other) noexcept = default;
|
||||
|
||||
// Operador de movimiento
|
||||
SurfaceData &operator=(SurfaceData &&other) noexcept = default;
|
||||
|
||||
// Evita copias accidentales
|
||||
SurfaceData(const SurfaceData &) = delete;
|
||||
SurfaceData &operator=(const SurfaceData &) = delete;
|
||||
};
|
||||
|
||||
class Surface
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<SurfaceData> surface_data_; // Datos a dibujar
|
||||
Palette palette_; // Paleta para volcar la SurfaceData a una Textura
|
||||
SubPalette sub_palette_; // Paleta para reindexar colores
|
||||
int transparent_color_; // Indice de la paleta que se omite en la copia de datos
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
Surface(int w, int h);
|
||||
explicit Surface(const std::string &file_path);
|
||||
|
||||
// Destructor
|
||||
~Surface() = default;
|
||||
|
||||
// Carga una SurfaceData desde un archivo
|
||||
SurfaceData loadSurface(const std::string &file_path);
|
||||
|
||||
// Carga una paleta desde un archivo
|
||||
void loadPalette(const std::string &file_path);
|
||||
void loadPalette(Palette palette);
|
||||
|
||||
// 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 x, int y, SDL_Rect *clip = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
|
||||
void render(SDL_Rect *srcRect = nullptr, SDL_Rect *dstRect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
|
||||
|
||||
// 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_FlipMode flip = SDL_FLIP_NONE);
|
||||
|
||||
// Establece un color en la paleta
|
||||
void setColor(int index, Uint32 color);
|
||||
|
||||
// Rellena la SurfaceData con un color
|
||||
void clear(Uint8 color);
|
||||
|
||||
// Vuelca la SurfaceData a una textura
|
||||
void copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture);
|
||||
void copyToTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_FRect *srcRect, SDL_FRect *destRect);
|
||||
|
||||
// Realiza un efecto de fundido en las paletas
|
||||
bool fadePalette();
|
||||
bool fadeSubPalette(Uint32 delay = 0);
|
||||
|
||||
// Pone un pixel en la SurfaceData
|
||||
void putPixel(int x, int y, Uint8 color);
|
||||
|
||||
// Obtiene el color de un pixel de la surface_data
|
||||
Uint8 getPixel(int x, int y);
|
||||
|
||||
// Dibuja un rectangulo relleno
|
||||
void fillRect(const SDL_Rect *rect, Uint8 color);
|
||||
|
||||
// Dibuja el borde de un rectangulo
|
||||
void drawRectBorder(const SDL_Rect *rect, Uint8 color);
|
||||
|
||||
// Dibuja una linea
|
||||
void drawLine(int x1, int y1, int x2, int y2, Uint8 color);
|
||||
|
||||
// Metodos para gestionar surface_data_
|
||||
std::shared_ptr<SurfaceData> getSurfaceData() const { return surface_data_; }
|
||||
void setSurfaceData(std::shared_ptr<SurfaceData> new_data) { surface_data_ = new_data; }
|
||||
|
||||
// Obtien ancho y alto
|
||||
int getWidth() const { return surface_data_->width; }
|
||||
int getHeight() const { return surface_data_->height; }
|
||||
|
||||
// Color transparente
|
||||
Uint8 getTransparentColor() const { return transparent_color_; }
|
||||
void setTransparentColor(Uint8 color = 255) { transparent_color_ = color; }
|
||||
|
||||
// Paleta
|
||||
void setPalette(const std::array<Uint32, 256> &palette) { palette_ = palette; }
|
||||
|
||||
// Inicializa la sub paleta
|
||||
void initializeSubPalette(SubPalette &palette) { std::iota(palette.begin(), palette.end(), 0); }
|
||||
};
|
||||
443
source/utils.cpp
Normal file
443
source/utils.cpp
Normal file
@@ -0,0 +1,443 @@
|
||||
#include "utils.h"
|
||||
#include <stdlib.h> // Para abs
|
||||
#include <algorithm> // Para find, transform
|
||||
#include <cctype> // Para tolower
|
||||
#include <cmath> // Para round, abs
|
||||
#include <exception> // Para exception
|
||||
#include <filesystem> // Para path
|
||||
#include <iostream> // Para basic_ostream, cout, basic_ios, ios, endl
|
||||
#include <string> // Para basic_string, string, char_traits, allocator
|
||||
#include <unordered_map> // Para unordered_map, operator==, _Node_const_iter...
|
||||
#include <utility> // Para pair
|
||||
#include "jail_audio.h" // Para JA_GetMusicState, JA_Music_state, JA_PlayMusic
|
||||
|
||||
// Calcula el cuadrado de la distancia entre dos puntos
|
||||
double distanceSquared(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
const int deltaX = x2 - x1;
|
||||
const int deltaY = y2 - y1;
|
||||
return deltaX * deltaX + deltaY * deltaY;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre dos circulos
|
||||
bool checkCollision(const Circle &a, const Circle &b)
|
||||
{
|
||||
// Calcula el radio total al cuadrado
|
||||
int totalRadiusSquared = a.r + b.r;
|
||||
totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;
|
||||
|
||||
// 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))
|
||||
{
|
||||
// Los circulos han colisionado
|
||||
return true;
|
||||
}
|
||||
|
||||
// En caso contrario
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre un circulo y un rectangulo
|
||||
bool checkCollision(const Circle &a, const SDL_Rect &b)
|
||||
{
|
||||
// Closest point on collision box
|
||||
int cX, cY;
|
||||
|
||||
// Find closest x offset
|
||||
if (a.x < b.x)
|
||||
{
|
||||
cX = b.x;
|
||||
}
|
||||
else if (a.x > b.x + b.w)
|
||||
{
|
||||
cX = b.x + b.w;
|
||||
}
|
||||
else
|
||||
{
|
||||
cX = a.x;
|
||||
}
|
||||
|
||||
// Find closest y offset
|
||||
if (a.y < b.y)
|
||||
{
|
||||
cY = b.y;
|
||||
}
|
||||
else if (a.y > b.y + b.h)
|
||||
{
|
||||
cY = b.y + b.h;
|
||||
}
|
||||
else
|
||||
{
|
||||
cY = a.y;
|
||||
}
|
||||
|
||||
// If the closest point is inside the circle_t
|
||||
if (distanceSquared(a.x, a.y, cX, cY) < a.r * a.r)
|
||||
{
|
||||
// This box and the circle_t have collided
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the shapes have not collided
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre dos rectangulos
|
||||
bool checkCollision(const SDL_Rect &a, const SDL_Rect &b)
|
||||
{
|
||||
// Calcula las caras del rectangulo a
|
||||
const int leftA = a.x;
|
||||
const int rightA = a.x + a.w;
|
||||
const int topA = a.y;
|
||||
const int bottomA = a.y + a.h;
|
||||
|
||||
// Calcula las caras del rectangulo b
|
||||
const int leftB = b.x;
|
||||
const int rightB = b.x + b.w;
|
||||
const int topB = b.y;
|
||||
const int bottomB = b.y + b.h;
|
||||
|
||||
// Si cualquiera de las caras de a está fuera de b
|
||||
if (bottomA <= topB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (topA >= bottomB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rightA <= leftB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (leftA >= rightB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si ninguna de las caras está fuera de b
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre un punto y un rectangulo
|
||||
bool checkCollision(const SDL_Point &p, const SDL_Rect &r)
|
||||
{
|
||||
// Comprueba si el punto está a la izquierda del rectangulo
|
||||
if (p.x < r.x)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el punto está a la derecha del rectangulo
|
||||
if (p.x > r.x + r.w)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el punto está por encima del rectangulo
|
||||
if (p.y < r.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el punto está por debajo del rectangulo
|
||||
if (p.y > r.y + r.h)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si no está fuera, es que está dentro
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre una linea horizontal y un rectangulo
|
||||
bool checkCollision(const LineHorizontal &l, const SDL_Rect &r)
|
||||
{
|
||||
// Comprueba si la linea esta por encima del rectangulo
|
||||
if (l.y < r.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si la linea esta por debajo del rectangulo
|
||||
if (l.y >= r.y + r.h)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el inicio de la linea esta a la derecha del rectangulo
|
||||
if (l.x1 >= r.x + r.w)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el final de la linea esta a la izquierda del rectangulo
|
||||
if (l.x2 < r.x)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si ha llegado hasta aquí, hay colisión
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre una linea vertical y un rectangulo
|
||||
bool checkCollision(const LineVertical &l, const SDL_Rect &r)
|
||||
{
|
||||
// Comprueba si la linea esta por la izquierda del rectangulo
|
||||
if (l.x < r.x)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si la linea esta por la derecha del rectangulo
|
||||
if (l.x >= r.x + r.w)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el inicio de la linea esta debajo del rectangulo
|
||||
if (l.y1 >= r.y + r.h)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el final de la linea esta encima del rectangulo
|
||||
if (l.y2 < r.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si ha llegado hasta aquí, hay colisión
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre una linea horizontal y un punto
|
||||
bool checkCollision(const LineHorizontal &l, const SDL_Point &p)
|
||||
{
|
||||
// Comprueba si el punto esta sobre la linea
|
||||
if (p.y > l.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el punto esta bajo la linea
|
||||
if (p.y < l.y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el punto esta a la izquierda de la linea
|
||||
if (p.x < l.x1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si el punto esta a la derecha de la linea
|
||||
if (p.x > l.x2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si ha llegado aquí, hay colisión
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detector de colisiones entre dos lineas
|
||||
SDL_Point checkCollision(const Line &l1, const Line &l2)
|
||||
{
|
||||
const float x1 = l1.x1;
|
||||
const float y1 = l1.y1;
|
||||
const float x2 = l1.x2;
|
||||
const float y2 = l1.y2;
|
||||
|
||||
const float x3 = l2.x1;
|
||||
const float y3 = l2.y1;
|
||||
const float x4 = l2.x2;
|
||||
const float y4 = l2.y2;
|
||||
|
||||
// calculate the direction of the lines
|
||||
float uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (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 >= 0 && uA <= 1 && uB >= 0 && uB <= 1)
|
||||
{
|
||||
// Calcula la intersección
|
||||
const float x = x1 + (uA * (x2 - x1));
|
||||
const float y = y1 + (uA * (y2 - y1));
|
||||
|
||||
return {(int)round(x), (int)round(y)};
|
||||
}
|
||||
return {-1, -1};
|
||||
}
|
||||
|
||||
// Detector de colisiones entre dos lineas
|
||||
SDL_Point checkCollision(const LineDiagonal &l1, const LineVertical &l2)
|
||||
{
|
||||
const float x1 = l1.x1;
|
||||
const float y1 = l1.y1;
|
||||
const float x2 = l1.x2;
|
||||
const float y2 = l1.y2;
|
||||
|
||||
const float x3 = l2.x;
|
||||
const float y3 = l2.y1;
|
||||
const float x4 = l2.x;
|
||||
const float y4 = l2.y2;
|
||||
|
||||
// calculate the direction of the lines
|
||||
float uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (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 >= 0 && uA <= 1 && uB >= 0 && uB <= 1)
|
||||
{
|
||||
// Calcula la intersección
|
||||
const float x = x1 + (uA * (x2 - x1));
|
||||
const float y = y1 + (uA * (y2 - y1));
|
||||
|
||||
return {(int)x, (int)y};
|
||||
}
|
||||
return {-1, -1};
|
||||
}
|
||||
|
||||
// Normaliza una linea diagonal
|
||||
void normalizeLine(LineDiagonal &l)
|
||||
{
|
||||
// Las lineas diagonales van de izquierda a derecha
|
||||
// x2 mayor que x1
|
||||
if (l.x2 < l.x1)
|
||||
{
|
||||
const int x = l.x1;
|
||||
const int y = l.y1;
|
||||
l.x1 = l.x2;
|
||||
l.y1 = l.y2;
|
||||
l.x2 = x;
|
||||
l.y2 = y;
|
||||
}
|
||||
}
|
||||
|
||||
// Detector de colisiones entre un punto y una linea diagonal
|
||||
bool checkCollision(const SDL_Point &p, const LineDiagonal &l)
|
||||
{
|
||||
// Comprueba si el punto está en alineado con la linea
|
||||
if (abs(p.x - l.x1) != abs(p.y - l.y1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si está a la derecha de la linea
|
||||
if (p.x > l.x1 && p.x > l.x2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si está a la izquierda de la linea
|
||||
if (p.x < l.x1 && p.x < l.x2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si está por encima de la linea
|
||||
if (p.y > l.y1 && p.y > l.y2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Comprueba si está por debajo de la linea
|
||||
if (p.y < l.y1 && p.y < l.y2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// En caso contrario, el punto está en la linea
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convierte una cadena a un entero de forma segura
|
||||
int safeStoi(const std::string &value, int defaultValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
return std::stoi(value);
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Convierte una cadena a un booleano
|
||||
bool stringToBool(const std::string &str)
|
||||
{
|
||||
std::string lowerStr = str;
|
||||
std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower);
|
||||
return (lowerStr == "true" || lowerStr == "1" || lowerStr == "yes" || lowerStr == "on");
|
||||
}
|
||||
|
||||
// Convierte un booleano a una cadena
|
||||
std::string boolToString(bool value)
|
||||
{
|
||||
return value ? "1" : "0";
|
||||
}
|
||||
|
||||
// Compara dos colores
|
||||
bool colorAreEqual(Color color1, Color color2)
|
||||
{
|
||||
const bool r = color1.r == color2.r;
|
||||
const bool g = color1.g == color2.g;
|
||||
const bool b = color1.b == color2.b;
|
||||
|
||||
return (r && g && b);
|
||||
}
|
||||
|
||||
// Función para convertir un string a minúsculas
|
||||
std::string toLower(const std::string &str)
|
||||
{
|
||||
std::string lower_str = str;
|
||||
std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(), ::tolower);
|
||||
return lower_str;
|
||||
}
|
||||
|
||||
// Función para convertir un string a mayúsculas
|
||||
std::string toUpper(const std::string &str)
|
||||
{
|
||||
std::string upper_str = str;
|
||||
std::transform(upper_str.begin(), upper_str.end(), upper_str.begin(), ::toupper);
|
||||
return upper_str;
|
||||
}
|
||||
|
||||
// Obtiene el nombre de un fichero a partir de una ruta completa
|
||||
std::string getFileName(const std::string &path)
|
||||
{
|
||||
return std::filesystem::path(path).filename().string();
|
||||
}
|
||||
|
||||
// Obtiene la ruta eliminando el nombre del fichero
|
||||
std::string getPath(const std::string &full_path)
|
||||
{
|
||||
std::filesystem::path path(full_path);
|
||||
return path.parent_path().string();
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
std::cout.setf(std::ios::left, std::ios::adjustfield);
|
||||
std::cout << text1;
|
||||
|
||||
std::cout.width(50 - text1.length() - text3.length());
|
||||
std::cout.fill('.');
|
||||
std::cout << text2;
|
||||
|
||||
std::cout << text3 << std::endl;
|
||||
}
|
||||
|
||||
// Comprueba si una vector contiene una cadena
|
||||
bool stringInVector(const std::vector<std::string> &vec, const std::string &str)
|
||||
{
|
||||
return std::find(vec.begin(), vec.end(), str) != vec.end();
|
||||
}
|
||||
118
source/utils.h
Normal file
118
source/utils.h
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_Rect, SDL_Point
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
// Estructura para definir un circulo
|
||||
struct Circle
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int r;
|
||||
};
|
||||
|
||||
// Estructura para definir una linea horizontal
|
||||
struct LineHorizontal
|
||||
{
|
||||
int x1, x2, y;
|
||||
};
|
||||
|
||||
// Estructura para definir una linea vertical
|
||||
struct LineVertical
|
||||
{
|
||||
int x, y1, y2;
|
||||
};
|
||||
|
||||
// Estructura para definir una linea diagonal
|
||||
struct LineDiagonal
|
||||
{
|
||||
int x1, y1, x2, y2;
|
||||
};
|
||||
|
||||
// Estructura para definir una linea
|
||||
struct Line
|
||||
{
|
||||
int x1, y1, x2, y2;
|
||||
};
|
||||
|
||||
// Estructura para definir un color
|
||||
struct Color
|
||||
{
|
||||
Uint8 r;
|
||||
Uint8 g;
|
||||
Uint8 b;
|
||||
|
||||
// Constructor por defecto
|
||||
Color() : r(0), g(0), b(0) {}
|
||||
|
||||
// Constructor
|
||||
Color(Uint8 red, Uint8 green, Uint8 blue)
|
||||
: r(red), g(green), b(blue) {}
|
||||
};
|
||||
|
||||
// Calcula el cuadrado de la distancia entre dos puntos
|
||||
double distanceSquared(int x1, int y1, int x2, int y2);
|
||||
|
||||
// Detector de colisiones entre dos circulos
|
||||
bool checkCollision(const Circle &a, const Circle &b);
|
||||
|
||||
// Detector de colisiones entre un circulo y un rectangulo
|
||||
bool checkCollision(const Circle &a, const SDL_Rect &b);
|
||||
|
||||
// Detector de colisiones entre un dos rectangulos
|
||||
bool checkCollision(const SDL_Rect &a, const SDL_Rect &b);
|
||||
|
||||
// Detector de colisiones entre un punto y un rectangulo
|
||||
bool checkCollision(const SDL_Point &p, const SDL_Rect &r);
|
||||
|
||||
// Detector de colisiones entre una linea horizontal y un rectangulo
|
||||
bool checkCollision(const LineHorizontal &l, const SDL_Rect &r);
|
||||
|
||||
// Detector de colisiones entre una linea vertical y un rectangulo
|
||||
bool checkCollision(const LineVertical &l, const SDL_Rect &r);
|
||||
|
||||
// Detector de colisiones entre una linea horizontal y un punto
|
||||
bool checkCollision(const LineHorizontal &l, const SDL_Point &p);
|
||||
|
||||
// Detector de colisiones entre dos lineas
|
||||
SDL_Point checkCollision(const Line &l1, const Line &l2);
|
||||
|
||||
// Detector de colisiones entre dos lineas
|
||||
SDL_Point checkCollision(const LineDiagonal &l1, const LineVertical &l2);
|
||||
|
||||
// Detector de colisiones entre un punto y una linea diagonal
|
||||
bool checkCollision(const SDL_Point &p, const LineDiagonal &l);
|
||||
|
||||
// Normaliza una linea diagonal
|
||||
void normalizeLine(LineDiagonal &l);
|
||||
|
||||
// Convierte una cadena a un entero de forma segura
|
||||
int safeStoi(const std::string &value, int defaultValue = 0);
|
||||
|
||||
// Convierte una cadena a un booleano
|
||||
bool stringToBool(const std::string &str);
|
||||
|
||||
// Convierte un booleano a una cadena
|
||||
std::string boolToString(bool value);
|
||||
|
||||
// Compara dos colores
|
||||
bool colorAreEqual(Color color1, Color color2);
|
||||
|
||||
// Convierte una cadena a minusculas
|
||||
std::string toLower(const std::string &str);
|
||||
|
||||
// Convierte una cadena a mayúsculas
|
||||
std::string toUpper(const std::string &str);
|
||||
|
||||
// Obtiene el nombre de un fichero a partir de una ruta
|
||||
std::string getFileName(const std::string &path);
|
||||
|
||||
// Obtiene la ruta eliminando el nombre del fichero
|
||||
std::string getPath(const std::string &full_path);
|
||||
|
||||
// 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);
|
||||
|
||||
// Comprueba si una vector contiene una cadena
|
||||
bool stringInVector(const std::vector<std::string> &vec, const std::string &str);
|
||||
Reference in New Issue
Block a user