migrat jail_audio a un .hpp (segur que ja no funciona res)

This commit is contained in:
2025-11-17 12:27:58 +01:00
parent 6827512025
commit 21a495e40a
11 changed files with 583 additions and 1065 deletions

View File

@@ -108,11 +108,6 @@ set(APP_SOURCES
source/main.cpp source/main.cpp
) )
# Fuentes de librerías de terceros
set(EXTERNAL_SOURCES
source/external/jail_audio.cpp
)
# Fuentes del sistema de renderizado # Fuentes del sistema de renderizado
set(RENDERING_SOURCES set(RENDERING_SOURCES
source/core/rendering/opengl/opengl_shader.cpp source/core/rendering/opengl/opengl_shader.cpp
@@ -128,7 +123,7 @@ find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}") message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
# --- 2. AÑADIR EJECUTABLE --- # --- 2. AÑADIR EJECUTABLE ---
add_executable(${PROJECT_NAME} ${APP_SOURCES} ${EXTERNAL_SOURCES} ${RENDERING_SOURCES}) add_executable(${PROJECT_NAME} ${APP_SOURCES} ${RENDERING_SOURCES})
# Añadir fuentes de debug solo en modo Debug # Añadir fuentes de debug solo en modo Debug
target_sources(${PROJECT_NAME} PRIVATE $<$<CONFIG:Debug>:${DEBUG_SOURCES}>) target_sources(${PROJECT_NAME} PRIVATE $<$<CONFIG:Debug>:${DEBUG_SOURCES}>)

View File

@@ -1,187 +0,0 @@
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(jaildoctors_dilemma VERSION 1.00)
# Establecer estándar de C++
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# Exportar comandos de compilación para herramientas de análisis
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Establece la política CMP0072 para indicar cómo se debe seleccionar la implementación de OpenGL.
# En este caso, se elige la opción "GLVND", que utiliza bibliotecas modernas y modulares (libOpenGL, libGLX),
# en lugar de la biblioteca OpenGL clásica (libGL). Esto mejora la compatibilidad con drivers recientes
# y evita ambigüedades cuando se encuentran múltiples implementaciones de OpenGL en el sistema.
cmake_policy(SET CMP0072 NEW)
set(OpenGL_GL_PREFERENCE GLVND)
# --- GENERACIÓN DE VERSIÓN AUTOMÁTICA ---
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
else()
set(GIT_HASH "unknown")
endif()
# Configurar archivo de versión
configure_file(${CMAKE_SOURCE_DIR}/source/version.h.in ${CMAKE_BINARY_DIR}/version.h @ONLY)
# --- 1. LISTA EXPLÍCITA DE FUENTES ---
set(APP_SOURCES
# Core - Audio
source/core/audio/audio.cpp
# Core - Input
source/core/input/global_inputs.cpp
source/core/input/input.cpp
source/core/input/input_types.cpp
source/core/input/mouse.cpp
# Core - Rendering
source/core/rendering/gif.cpp
source/core/rendering/screen.cpp
source/core/rendering/surface.cpp
source/core/rendering/surface_animated_sprite.cpp
source/core/rendering/surface_moving_sprite.cpp
source/core/rendering/surface_sprite.cpp
source/core/rendering/text.cpp
source/core/rendering/texture.cpp
# Core - Resources
source/core/resources/resource_list.cpp
source/core/resources/resource_cache.cpp
source/core/resources/resource_pack.cpp
source/core/resources/resource_loader.cpp
source/core/resources/resource_helper.cpp
# Core - System
source/core/system/director.cpp
source/core/system/global_events.cpp
# Game - Entities
source/game/entities/enemy.cpp
source/game/entities/item.cpp
source/game/entities/player.cpp
# Game - Configuration
source/game/options.cpp
# Game - Gameplay
source/game/gameplay/cheevos.cpp
source/game/gameplay/collision_map.cpp
source/game/gameplay/enemy_manager.cpp
source/game/gameplay/item_manager.cpp
source/game/gameplay/item_tracker.cpp
source/game/gameplay/room.cpp
source/game/gameplay/room_loader.cpp
source/game/gameplay/room_tracker.cpp
source/game/gameplay/scoreboard.cpp
source/game/gameplay/stats.cpp
source/game/gameplay/tilemap_renderer.cpp
# Game - Scenes
source/game/scenes/credits.cpp
source/game/scenes/ending.cpp
source/game/scenes/ending2.cpp
source/game/scenes/game.cpp
source/game/scenes/game_over.cpp
source/game/scenes/loading_screen.cpp
source/game/scenes/logo.cpp
source/game/scenes/title.cpp
# Game - UI
source/game/ui/notifier.cpp
# Utils
source/utils/delta_timer.cpp
source/utils/utils.cpp
# Main
source/main.cpp
)
# Fuentes de librerías de terceros
set(EXTERNAL_SOURCES
source/external/jail_audio.cpp
)
# Fuentes del sistema de renderizado
set(RENDERING_SOURCES
source/core/rendering/opengl/opengl_shader.cpp
)
# Fuentes de debug (solo en modo Debug)
set(DEBUG_SOURCES
source/core/system/debug.cpp
)
# Configuración de SDL3
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
message(STATUS "SDL3 encontrado: ${SDL3_INCLUDE_DIRS}")
# Configuración de yaml-cpp (desde source/external/)
# Deshabilitar tests y herramientas de yaml-cpp
set(YAML_CPP_BUILD_TESTS OFF CACHE INTERNAL "")
set(YAML_CPP_BUILD_TOOLS OFF CACHE INTERNAL "")
set(YAML_CPP_BUILD_CONTRIB OFF CACHE INTERNAL "")
set(YAML_CPP_INSTALL OFF CACHE INTERNAL "")
add_subdirectory(source/external/yaml-cpp)
message(STATUS "yaml-cpp configurado desde source/external/")
# --- 2. AÑADIR EJECUTABLE ---
add_executable(${PROJECT_NAME} ${APP_SOURCES} ${EXTERNAL_SOURCES} ${RENDERING_SOURCES})
# Añadir fuentes de debug solo en modo Debug
target_sources(${PROJECT_NAME} PRIVATE $<$<CONFIG:Debug>:${DEBUG_SOURCES}>)
# --- 3. DIRECTORIOS DE INCLUSIÓN ---
target_include_directories(${PROJECT_NAME} PUBLIC
"${CMAKE_SOURCE_DIR}/source"
"${CMAKE_BINARY_DIR}"
)
# Enlazar las librerías SDL3 y yaml-cpp
target_link_libraries(${PROJECT_NAME} PRIVATE SDL3::SDL3 yaml-cpp::yaml-cpp)
# --- 4. CONFIGURACIÓN PLATAFORMAS Y COMPILADOR ---
# Configuración de flags de compilación
target_compile_options(${PROJECT_NAME} PRIVATE -Wall)
target_compile_options(${PROJECT_NAME} PRIVATE $<$<CONFIG:RELEASE>:-Os -ffunction-sections -fdata-sections>)
# Definir _DEBUG en modo Debug
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<CONFIG:DEBUG>:_DEBUG>)
# Configuración específica para cada plataforma
if(WIN32)
target_compile_definitions(${PROJECT_NAME} PRIVATE WINDOWS_BUILD)
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32 mingw32 opengl32)
elseif(APPLE)
target_compile_definitions(${PROJECT_NAME} PRIVATE MACOS_BUILD)
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-deprecated)
set(CMAKE_OSX_ARCHITECTURES "arm64")
elseif(UNIX AND NOT APPLE)
target_compile_definitions(${PROJECT_NAME} PRIVATE LINUX_BUILD)
endif()
# Configuración común para OpenGL
if(NOT WIN32)
find_package(OpenGL REQUIRED)
if(OPENGL_FOUND)
message(STATUS "OpenGL encontrado: ${OPENGL_LIBRARIES}")
target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENGL_LIBRARIES})
else()
message(FATAL_ERROR "OpenGL no encontrado")
endif()
endif()
# Especificar la ubicación del ejecutable
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR})

View File

@@ -109,19 +109,15 @@ APP_SOURCES := \
source/game/scenes/credits.cpp \ source/game/scenes/credits.cpp \
source/game/ui/notifier.cpp \ source/game/ui/notifier.cpp \
source/utils/utils.cpp \ source/utils/utils.cpp \
source/utils/delta_timer.cpp \ source/utils/delta_timer.cpp
source/external/jail_audio.cpp
# yaml-cpp library sources
YAML_CPP_SOURCES := $(wildcard source/external/yaml-cpp/src/*.cpp)
# All sources combined # All sources combined
ALL_SOURCES := $(APP_SOURCES) $(YAML_CPP_SOURCES) ALL_SOURCES := $(APP_SOURCES)
# ============================================================================== # ==============================================================================
# INCLUDES # INCLUDES
# ============================================================================== # ==============================================================================
INCLUDES := -Isource -Isource/external/yaml-cpp/include INCLUDES := -Isource
# ============================================================================== # ==============================================================================
# COMPILER FLAGS (OS-specific) # COMPILER FLAGS (OS-specific)

View File

@@ -5,8 +5,14 @@
#include <algorithm> // Para clamp #include <algorithm> // Para clamp
#include <iostream> // Para std::cout #include <iostream> // Para std::cout
// Implementación de stb_vorbis (debe estar ANTES de incluir jail_audio.hpp)
// clang-format off
#undef STB_VORBIS_HEADER_ONLY
#include "external/stb_vorbis.h"
// clang-format on
#include "core/audio/jail_audio.hpp" // Para JA_FadeOutMusic, JA_Init, JA_PauseM...
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
#include "external/jail_audio.h" // Para JA_FadeOutMusic, JA_Init, JA_PauseM...
#include "game/options.hpp" // Para AudioOptions, audio, MusicOptions #include "game/options.hpp" // Para AudioOptions, audio, MusicOptions
// Singleton // Singleton

View File

@@ -0,0 +1,529 @@
#pragma once
// --- Includes ---
#include <SDL3/SDL.h>
#include <stdint.h> // Para uint32_t, uint8_t
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
#include <stdlib.h> // Para free, malloc
#include <string.h> // Para strcpy, strlen
#define STB_VORBIS_HEADER_ONLY
#include "external/stb_vorbis.h" // Para stb_vorbis_decode_memory
// --- Public Enums ---
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 Definitions ---
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
#define JA_MAX_GROUPS 2
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 };
int group { 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 };
char *filename { nullptr };
int pos { 0 };
int times { 0 };
SDL_AudioStream *stream { nullptr };
JA_Music_state state { JA_MUSIC_INVALID };
};
// --- Internal Global State ---
// Marcado 'inline' (C++17) para asegurar una única instancia.
inline JA_Music_t* current_music { nullptr };
inline JA_Channel_t channels[JA_MAX_SIMULTANEOUS_CHANNELS];
inline SDL_AudioSpec JA_audioSpec { SDL_AUDIO_S16, 2, 48000 };
inline float JA_musicVolume { 1.0f };
inline float JA_soundVolume[JA_MAX_GROUPS];
inline bool JA_musicEnabled { true };
inline bool JA_soundEnabled { true };
inline SDL_AudioDeviceID sdlAudioDevice { 0 };
inline bool fading { false };
inline int fade_start_time { 0 };
inline int fade_duration { 0 };
inline float fade_initial_volume { 0.0f }; // Corregido de 'int' a 'float'
// --- Forward Declarations ---
inline void JA_StopMusic();
inline void JA_StopChannel(const int channel);
inline int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop = 0, const int group = 0);
// --- Core Functions ---
inline void JA_Update()
{
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;
} else {
const int time_passed = time - fade_start_time;
const float percent = (float)time_passed / (float)fade_duration;
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * (1.0 - percent));
}
}
if (current_music->times != 0)
{
if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length / 2)) {
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 ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) {
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
if (channels[i].times > 0) channels[i].times--;
}
}
else
{
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
}
}
}
}
inline void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels)
{
#ifdef _DEBUG
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
#endif
JA_audioSpec = { format, num_channels, freq };
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); // Corregido: !sdlAudioDevice -> sdlAudioDevice
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
if (sdlAudioDevice == 0) SDL_Log("Failed to initialize SDL audio!");
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) channels[i].state = JA_CHANNEL_FREE;
for (int i = 0; i < JA_MAX_GROUPS; ++i) JA_soundVolume[i] = 0.5f;
}
inline void JA_Quit()
{
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice); // Corregido: !sdlAudioDevice -> sdlAudioDevice
sdlAudioDevice = 0;
}
// --- Music Functions ---
inline 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;
}
inline 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");
if (!f) return NULL; // Añadida comprobación de apertura
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
Uint8 *buffer = (Uint8*)malloc(fsize + 1);
if (!buffer) { // Añadida comprobación de malloc
fclose(f);
return NULL;
}
if (fread(buffer, fsize, 1, f) != 1) {
fclose(f);
free(buffer);
return NULL;
}
fclose(f);
JA_Music_t *music = JA_LoadMusic(buffer, fsize);
if (music) { // Comprobar que JA_LoadMusic tuvo éxito
music->filename = (char*)malloc(strlen(filename) + 1);
if (music->filename) {
strcpy(music->filename, filename);
}
}
free(buffer);
return music;
}
inline void JA_PlayMusic(JA_Music_t *music, const int loop = -1)
{
if (!JA_musicEnabled || !music) return; // Añadida comprobación de music
JA_StopMusic();
current_music = music;
current_music->pos = 0;
current_music->state = JA_MUSIC_PLAYING;
current_music->times = loop;
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &JA_audioSpec);
if (!current_music->stream) { // Comprobar creación de stream
SDL_Log("Failed to create audio stream!");
current_music->state = JA_MUSIC_STOPPED;
return;
}
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");
}
inline char *JA_GetMusicFilename(JA_Music_t *music = nullptr)
{
if (!music) music = current_music;
if (!music) return nullptr; // Añadida comprobación
return music->filename;
}
inline void JA_PauseMusic()
{
if (!JA_musicEnabled) return;
if (!current_music || current_music->state != JA_MUSIC_PLAYING) return; // Comprobación mejorada
current_music->state = JA_MUSIC_PAUSED;
SDL_UnbindAudioStream(current_music->stream);
}
inline void JA_ResumeMusic()
{
if (!JA_musicEnabled) return;
if (!current_music || current_music->state != JA_MUSIC_PAUSED) return; // Comprobación mejorada
current_music->state = JA_MUSIC_PLAYING;
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
}
inline void JA_StopMusic()
{
if (!current_music || current_music->state == JA_MUSIC_INVALID || current_music->state == JA_MUSIC_STOPPED) return;
current_music->pos = 0;
current_music->state = JA_MUSIC_STOPPED;
if (current_music->stream) {
SDL_DestroyAudioStream(current_music->stream);
current_music->stream = nullptr;
}
// No liberamos filename aquí, se debería liberar en JA_DeleteMusic
}
inline 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;
}
inline JA_Music_state JA_GetMusicState()
{
if (!JA_musicEnabled) return JA_MUSIC_DISABLED;
if (!current_music) return JA_MUSIC_INVALID;
return current_music->state;
}
inline void JA_DeleteMusic(JA_Music_t *music)
{
if (!music) return;
if (current_music == music) {
JA_StopMusic();
current_music = nullptr;
}
SDL_free(music->buffer);
if (music->stream) SDL_DestroyAudioStream(music->stream);
free(music->filename); // filename se libera aquí
delete music;
}
inline float JA_SetMusicVolume(float volume)
{
JA_musicVolume = SDL_clamp(volume, 0.0f, 1.0f);
if (current_music && current_music->stream) {
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
}
return JA_musicVolume;
}
inline void JA_SetMusicPosition(float value)
{
if (!current_music) return;
current_music->pos = value * current_music->spec.freq;
// Nota: Esta implementación de 'pos' no parece usarse en JA_Update para
// el streaming. El streaming siempre parece empezar desde el principio.
}
inline float JA_GetMusicPosition()
{
if (!current_music) return 0;
return float(current_music->pos) / float(current_music->spec.freq);
// Nota: Ver `JA_SetMusicPosition`
}
inline void JA_EnableMusic(const bool value)
{
if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING)) JA_StopMusic();
JA_musicEnabled = value;
}
// --- Sound Functions ---
inline JA_Sound_t *JA_NewSound(Uint8* buffer, Uint32 length)
{
JA_Sound_t *sound = new JA_Sound_t();
sound->buffer = buffer;
sound->length = length;
// Nota: spec se queda con los valores por defecto.
return sound;
}
inline JA_Sound_t *JA_LoadSound(uint8_t* buffer, uint32_t size)
{
JA_Sound_t *sound = new JA_Sound_t();
if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &sound->buffer, &sound->length)) {
SDL_Log("Failed to load WAV from memory: %s", SDL_GetError());
delete sound;
return nullptr;
}
return sound;
}
inline JA_Sound_t *JA_LoadSound(const char* filename)
{
JA_Sound_t *sound = new JA_Sound_t();
if (!SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length)) {
SDL_Log("Failed to load WAV file: %s", SDL_GetError());
delete sound;
return nullptr;
}
return sound;
}
inline int JA_PlaySound(JA_Sound_t *sound, const int loop = 0, const int group = 0)
{
if (!JA_soundEnabled || !sound) return -1;
int channel = 0;
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; }
if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) {
// No hay canal libre, reemplazamos el primero
channel = 0;
}
return JA_PlaySoundOnChannel(sound, channel, loop, group);
}
inline int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group)
{
if (!JA_soundEnabled || !sound) return -1;
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) return -1;
JA_StopChannel(channel); // Detiene y limpia el canal si estaba en uso
channels[channel].sound = sound;
channels[channel].times = loop;
channels[channel].pos = 0;
channels[channel].group = group; // Asignar grupo
channels[channel].state = JA_CHANNEL_PLAYING;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
if (!channels[channel].stream) {
SDL_Log("Failed to create audio stream for sound!");
channels[channel].state = JA_CHANNEL_FREE;
return -1;
}
SDL_PutAudioStreamData(channels[channel].stream, channels[channel].sound->buffer, channels[channel].sound->length);
SDL_SetAudioStreamGain(channels[channel].stream, JA_soundVolume[group]);
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
return channel;
}
inline void JA_DeleteSound(JA_Sound_t *sound)
{
if (!sound) return;
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].sound == sound) JA_StopChannel(i);
}
SDL_free(sound->buffer);
delete sound;
}
inline 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_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_UnbindAudioStream(channels[channel].stream);
}
}
}
inline 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_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_BindAudioStream(sdlAudioDevice, channels[channel].stream);
}
}
}
inline void JA_StopChannel(const int channel)
{
if (channel == -1)
{
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].state != JA_CHANNEL_FREE) {
if (channels[i].stream) 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) {
if (channels[channel].stream) SDL_DestroyAudioStream(channels[channel].stream);
channels[channel].stream = nullptr;
channels[channel].state = JA_CHANNEL_FREE;
channels[channel].pos = 0;
channels[channel].sound = NULL;
}
}
}
inline 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;
}
inline float JA_SetSoundVolume(float volume, const int group = -1) // -1 para todos los grupos
{
const float v = SDL_clamp(volume, 0.0f, 1.0f);
if (group == -1) {
for (int i = 0; i < JA_MAX_GROUPS; ++i) {
JA_soundVolume[i] = v;
}
} else if (group >= 0 && group < JA_MAX_GROUPS) {
JA_soundVolume[group] = v;
} else {
return v; // Grupo inválido
}
// Aplicar volumen a canales activos
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED)) {
if (group == -1 || channels[i].group == group) {
if (channels[i].stream) {
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume[channels[i].group]);
}
}
}
}
return v;
}
inline void JA_EnableSound(const bool value)
{
if (!value) {
JA_StopChannel(-1); // Detener todos los canales
}
JA_soundEnabled = value;
}
inline float JA_SetVolume(float volume)
{
float v = JA_SetMusicVolume(volume);
JA_SetSoundVolume(v, -1); // Aplicar a todos los grupos de sonido
return v;
}

View File

@@ -8,11 +8,11 @@
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <utility> #include <utility>
#include "core/audio/jail_audio.hpp" // Para JA_DeleteMusic, JA_DeleteSound, JA_Loa...
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/text.hpp" // Para Text, loadTextFile #include "core/rendering/text.hpp" // Para Text, loadTextFile
#include "core/resources/resource_list.hpp" // Para List, List::Type
#include "core/resources/resource_helper.hpp" // Para Helper #include "core/resources/resource_helper.hpp" // Para Helper
#include "external/jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_Loa... #include "core/resources/resource_list.hpp" // Para List, List::Type
#include "game/defaults.hpp" // Para GameDefaults::VERSION #include "game/defaults.hpp" // Para GameDefaults::VERSION
#include "game/gameplay/room.hpp" // Para RoomData, loadRoomFile, loadRoomTileFile #include "game/gameplay/room.hpp" // Para RoomData, loadRoomFile, loadRoomTileFile
#include "game/options.hpp" // Para Options, OptionsGame, options #include "game/options.hpp" // Para Options, OptionsGame, options

View File

@@ -10,7 +10,7 @@ This directory contains third-party libraries used by JailDoctor's Dilemma.
| **stb_image** | v2.x | Header-only | Image loading (PNG, GIF) | | **stb_image** | v2.x | Header-only | Image loading (PNG, GIF) |
| **stb_vorbis** | v1.x | Header-only | OGG Vorbis audio decoding | | **stb_vorbis** | v1.x | Header-only | OGG Vorbis audio decoding |
| **json** | nlohmann/json | Header-only | JSON parsing (if needed) | | **json** | nlohmann/json | Header-only | JSON parsing (if needed) |
| **yaml-cpp** | v0.8.0 | Source library | YAML parsing for room files | | **fkYAML** | v3.x | Header-only | YAML parsing for room files |
## Structure ## Structure
@@ -21,11 +21,7 @@ external/
├── stb_image.h # STB image loader (header-only) ├── stb_image.h # STB image loader (header-only)
├── stb_vorbis.h # STB Vorbis decoder (header-only) ├── stb_vorbis.h # STB Vorbis decoder (header-only)
├── json.hpp # nlohmann JSON library (header-only) ├── json.hpp # nlohmann JSON library (header-only)
└── yaml-cpp/ # YAML parser library └── fkyaml_node.hpp # fkYAML parser library (header-only)
├── include/ # Public headers
│ └── yaml-cpp/
├── src/ # Source files (.cpp)
└── CMakeLists.txt # Build configuration
``` ```
## Why Dependencies Are Here ## Why Dependencies Are Here
@@ -42,28 +38,30 @@ All dependencies are kept in `source/external/` for several reasons:
### CMake (Recommended) ### CMake (Recommended)
```cmake ```cmake
# yaml-cpp # Include path for header-only libraries (fkYAML, json, stb)
add_subdirectory(source/external/yaml-cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE yaml-cpp::yaml-cpp)
# Include path
target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_SOURCE_DIR}/source") target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_SOURCE_DIR}/source")
# All external dependencies are header-only except jail_audio
# jail_audio.cpp is compiled as part of the main build
``` ```
### Makefile ### Makefile
See `Makefile.example` in the project root for a complete example. See `Makefile.example` in the project root for a complete example.
```makefile ```makefile
CXXFLAGS = -I./source -I./source/external/yaml-cpp/include CXXFLAGS = -I./source
# Compile yaml-cpp sources and link # Compile jail_audio.cpp and link with your sources
``` ```
### Manual Compilation ### Manual Compilation
```bash ```bash
# Compile yaml-cpp sources # Compile jail_audio (only non-header-only library)
g++ -c source/external/yaml-cpp/src/*.cpp -I./source/external/yaml-cpp/include g++ -c source/external/jail_audio.cpp -I./source
# Link with your project # Compile your project sources
g++ -c source/game/*.cpp -I./source
# Link everything together
g++ *.o -o game -lSDL3 g++ *.o -o game -lSDL3
``` ```
@@ -75,35 +73,34 @@ g++ *.o -o game -lSDL3
3. Include in code: `#include "external/library.h"` 3. Include in code: `#include "external/library.h"`
### Source Libraries ### Source Libraries
1. Create subdirectory: `source/external/library-name/` 1. Place source files (.cpp) directly in `source/external/`
2. Place headers in `library-name/include/` 2. Place header files (.h/.hpp) in `source/external/`
3. Place sources in `library-name/src/` 3. Add to CMakeLists.txt if needed:
4. Update CMakeLists.txt:
```cmake ```cmake
add_subdirectory(source/external/library-name) # Most dependencies are header-only
# For source libraries, add to SOURCES list
``` ```
## Updating Dependencies ## Updating Dependencies
### yaml-cpp ### fkYAML
To update yaml-cpp to a newer version: To update fkYAML to a newer version:
```bash ```bash
cd source/external cd source/external
rm -rf yaml-cpp # Download the single-header version from the releases page
git clone https://github.com/jbeder/yaml-cpp.git curl -L -O https://github.com/fktn-k/fkYAML/releases/download/v3.x.x/fkyaml_node.hpp
cd yaml-cpp
git checkout <version-tag> # e.g., 0.8.0
rm -rf .git # Remove git history to reduce size
``` ```
Or download from: https://github.com/fktn-k/fkYAML/releases
### STB Libraries ### STB Libraries
Download latest from: https://github.com/nothings/stb Download latest from: https://github.com/nothings/stb
```bash ```bash
cd source/external cd source/external
curl -O https://raw.githubusercontent.com/nothings/stb/master/stb_image.h curl -O https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
curl -O https://raw.githubusercontent.com/nothings/stb/master/stb_vorbis.c curl -O https://raw.githubusercontent.com/nothings/stb/master/stb_vorbis.h
``` ```
## License Information ## License Information
@@ -113,13 +110,13 @@ Each library has its own license:
- **jail_audio** - Custom (see source files) - **jail_audio** - Custom (see source files)
- **stb libraries** - Public Domain / MIT - **stb libraries** - Public Domain / MIT
- **nlohmann/json** - MIT License - **nlohmann/json** - MIT License
- **yaml-cpp** - MIT License - **fkYAML** - MIT License
See individual library directories/files for full license texts. See individual library files for full license texts.
## Notes ## Notes
- All dependencies are compatible with C++20 - All dependencies are compatible with C++20
- Header-only libraries require no compilation - Most libraries are header-only and require no compilation
- Source libraries (yaml-cpp, jail_audio) are compiled as part of the main build - Only jail_audio (source library) is compiled as part of the main build
- Keep dependencies minimal to reduce build times and binary size - Keep dependencies minimal to reduce build times and binary size

View File

@@ -1,477 +0,0 @@
#ifndef JA_USESDLMIXER
#include "jail_audio.h"
#include <SDL3/SDL.h> // Para SDL_AudioFormat, SDL_BindAudioStream, SDL_SetAudioStreamGain, SDL_PutAudioStreamData, SDL_DestroyAudioStream, SDL_GetAudioStreamAvailable, Uint8, SDL_CreateAudioStream, SDL_UnbindAudioStream, Uint32, SDL_CloseAudioDevice, SDL_GetTicks, SDL_Log, SDL_free, SDL_AudioSpec, SDL_AudioStream, SDL_IOFromMem, SDL_LoadWAV, SDL_LoadWAV_IO, SDL_OpenAudioDevice, SDL_clamp, SDL_malloc, SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, SDL_AudioDeviceID, SDL_memcpy
#include <stdint.h> // Para uint32_t, uint8_t
#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
#include <stdlib.h> // Para free, malloc
#include <string.h> // Para strcpy, strlen
#include "stb_vorbis.h" // Para stb_vorbis_decode_memory
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
#define JA_MAX_GROUPS 2
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 };
int group { 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 };
char *filename { 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[JA_MAX_GROUPS];
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 JA_Update()
{
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;
} else {
const int time_passed = time - fade_start_time;
const float percent = (float)time_passed / (float)fade_duration;
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume*(1.0 - percent));
}
}
if (current_music->times != 0)
{
if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length/2)) {
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 ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length/2)) {
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length);
if (channels[i].times>0) channels[i].times--;
}
}
else
{
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
}
}
}
return;
}
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels)
{
#ifdef _DEBUG
SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG);
#endif
JA_audioSpec = {format, num_channels, freq };
if (!sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
if (sdlAudioDevice==0) SDL_Log("Failed to initialize SDL audio!");
for (int i=0; i<JA_MAX_SIMULTANEOUS_CHANNELS; ++i) channels[i].state = JA_CHANNEL_FREE;
for (int i=0; i<JA_MAX_GROUPS; ++i) JA_soundVolume[i] = 0.5f;
}
void JA_Quit()
{
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);
music->filename = (char*)malloc(strlen(filename)+1);
strcpy(music->filename, filename);
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(&current_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");
//SDL_ResumeAudioStreamDevice(current_music->stream);
}
char *JA_GetMusicFilename(JA_Music_t *music)
{
if (!music) music = current_music;
return music->filename;
}
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;
free(current_music->filename);
current_music->filename = 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, const int group)
{
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[group]);
SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream);
return channel;
}
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group)
{
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[group]);
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, const int group)
{
const float v = SDL_clamp( volume, 0.0f, 1.0f );
for (int i = 0; i < JA_MAX_GROUPS; ++i) {
if (group==-1 || group==i) JA_soundVolume[i]=v;
}
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++)
if ( ((channels[i].state == JA_CHANNEL_PLAYING) || (channels[i].state == JA_CHANNEL_PAUSED)) &&
((group==-1) || (channels[i].group==group)) )
SDL_SetAudioStreamGain(channels[i].stream, JA_soundVolume[i]);
return v;
}
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

View File

@@ -1,43 +0,0 @@
#pragma once
#include <SDL3/SDL.h>
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_Update();
void JA_Init(const int freq, const SDL_AudioFormat format, const int num_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);
char *JA_GetMusicFilename(JA_Music_t *music = nullptr);
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, const int group=0);
int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop = 0, const int group=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, const int group=0);
void JA_EnableSound(const bool value);
float JA_SetVolume(float volume);

View File

@@ -1,297 +0,0 @@
#include "room_loader.hpp"
#include <exception> // Para exception
#include <iostream> // Para cout, cerr
#include <yaml-cpp/yaml.h> // Para YAML::Node, YAML::LoadFile
#include "core/resources/resource_helper.hpp" // Para Resource::Helper
#include "utils/defines.hpp" // Para TILE_SIZE
#include "utils/utils.hpp" // Para stringToColor
// Convierte room connection de YAML a formato con extensión
auto RoomLoader::convertRoomConnection(const std::string& value) -> std::string {
if (value == "null" || value.empty()) {
return "0";
}
// "02" → "02.yaml"
return value + ".yaml";
}
// Convierte un tilemap 2D a vector 1D flat
auto RoomLoader::flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d) -> std::vector<int> {
std::vector<int> tilemap_flat;
tilemap_flat.reserve(512); // 16 rows × 32 cols
for (const auto& row : tilemap_2d) {
for (int tile : row) {
tilemap_flat.push_back(tile);
}
}
return tilemap_flat;
}
// Carga un archivo de room en formato YAML
auto RoomLoader::loadYAML(const std::string& file_path, bool verbose) -> Room::Data {
Room::Data room;
// Extract filename for logging
const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1);
try {
// Load YAML file using ResourceHelper (supports both filesystem and pack)
auto file_data = Resource::Helper::loadFile(file_path);
if (file_data.empty()) {
std::cerr << "Error: Unable to load file " << FILE_NAME << '\n';
return room;
}
// Parse YAML from string
std::string yaml_content(file_data.begin(), file_data.end());
YAML::Node yaml = YAML::Load(yaml_content);
// --- Parse room configuration ---
if (yaml["room"]) {
const YAML::Node& room_node = yaml["room"];
// Extract room number from filename (e.g., "01.yaml" → "01")
room.number = FILE_NAME.substr(0, FILE_NAME.find_last_of('.'));
// Basic properties
if (room_node["name"]) {
room.name = room_node["name"].as<std::string>();
}
if (room_node["bgColor"]) {
room.bg_color = room_node["bgColor"].as<std::string>();
}
if (room_node["border"]) {
room.border_color = room_node["border"].as<std::string>();
}
if (room_node["tileSetFile"]) {
room.tile_set_file = room_node["tileSetFile"].as<std::string>();
}
// Room connections
if (room_node["connections"]) {
const YAML::Node& conn = room_node["connections"];
if (conn["up"]) {
room.upper_room = convertRoomConnection(conn["up"].as<std::string>("null"));
} else {
room.upper_room = "0";
}
if (conn["down"]) {
room.lower_room = convertRoomConnection(conn["down"].as<std::string>("null"));
} else {
room.lower_room = "0";
}
if (conn["left"]) {
room.left_room = convertRoomConnection(conn["left"].as<std::string>("null"));
} else {
room.left_room = "0";
}
if (conn["right"]) {
room.right_room = convertRoomConnection(conn["right"].as<std::string>("null"));
} else {
room.right_room = "0";
}
}
// Item colors
if (room_node["itemColor1"]) {
room.item_color1 = room_node["itemColor1"].as<std::string>("yellow");
} else {
room.item_color1 = "yellow";
}
if (room_node["itemColor2"]) {
room.item_color2 = room_node["itemColor2"].as<std::string>("magenta");
} else {
room.item_color2 = "magenta";
}
// Conveyor belt direction
if (room_node["autoSurface"]) {
room.conveyor_belt_direction = room_node["autoSurface"].as<int>(0);
} else {
room.conveyor_belt_direction = 0;
}
}
// --- Parse tilemap ---
if (yaml["tilemap"]) {
const YAML::Node& tilemap_node = yaml["tilemap"];
// Read 2D array
std::vector<std::vector<int>> tilemap_2d;
tilemap_2d.reserve(16);
for (const auto& row_node : tilemap_node) {
std::vector<int> row;
row.reserve(32);
for (const auto& tile_node : row_node) {
row.push_back(tile_node.as<int>());
}
tilemap_2d.push_back(row);
}
// Convert to 1D flat array
room.tile_map = flattenTilemap(tilemap_2d);
if (verbose) {
std::cout << "Loaded tilemap: " << room.tile_map.size() << " tiles\n";
}
} else {
std::cerr << "Warning: No tilemap found in " << FILE_NAME << '\n';
}
// --- Parse enemies ---
if (yaml["enemies"] && !yaml["enemies"].IsNull()) {
const YAML::Node& enemies_node = yaml["enemies"];
for (const auto& enemy_node : enemies_node) {
Enemy::Data enemy;
// Animation path
if (enemy_node["animation"]) {
enemy.animation_path = enemy_node["animation"].as<std::string>();
}
// Position (in tiles, convert to pixels)
if (enemy_node["position"]) {
const YAML::Node& pos = enemy_node["position"];
if (pos["x"]) {
enemy.x = pos["x"].as<float>() * TILE_SIZE;
}
if (pos["y"]) {
enemy.y = pos["y"].as<float>() * TILE_SIZE;
}
}
// Velocity (already in pixels/second)
if (enemy_node["velocity"]) {
const YAML::Node& vel = enemy_node["velocity"];
if (vel["x"]) {
enemy.vx = vel["x"].as<float>();
}
if (vel["y"]) {
enemy.vy = vel["y"].as<float>();
}
}
// Boundaries (in tiles, convert to pixels)
if (enemy_node["boundaries"]) {
const YAML::Node& bounds = enemy_node["boundaries"];
if (bounds["x1"]) {
enemy.x1 = bounds["x1"].as<int>() * TILE_SIZE;
}
if (bounds["y1"]) {
enemy.y1 = bounds["y1"].as<int>() * TILE_SIZE;
}
if (bounds["x2"]) {
enemy.x2 = bounds["x2"].as<int>() * TILE_SIZE;
}
if (bounds["y2"]) {
enemy.y2 = bounds["y2"].as<int>() * TILE_SIZE;
}
}
// Color
if (enemy_node["color"]) {
enemy.color = enemy_node["color"].as<std::string>("white");
} else {
enemy.color = "white";
}
// Optional fields
if (enemy_node["flip"]) {
enemy.flip = enemy_node["flip"].as<bool>(false);
} else {
enemy.flip = false;
}
if (enemy_node["mirror"]) {
enemy.mirror = enemy_node["mirror"].as<bool>(false);
} else {
enemy.mirror = false;
}
if (enemy_node["frame"]) {
enemy.frame = enemy_node["frame"].as<int>(-1);
} else {
enemy.frame = -1;
}
room.enemies.push_back(enemy);
}
if (verbose) {
std::cout << "Loaded " << room.enemies.size() << " enemies\n";
}
}
// --- Parse items ---
if (yaml["items"] && !yaml["items"].IsNull()) {
const YAML::Node& items_node = yaml["items"];
for (const auto& item_node : items_node) {
Item::Data item;
// Tileset file
if (item_node["tileSetFile"]) {
item.tile_set_file = item_node["tileSetFile"].as<std::string>();
}
// Tile index
if (item_node["tile"]) {
item.tile = item_node["tile"].as<int>();
}
// Position (in tiles, convert to pixels)
if (item_node["position"]) {
const YAML::Node& pos = item_node["position"];
if (pos["x"]) {
item.x = pos["x"].as<float>() * TILE_SIZE;
}
if (pos["y"]) {
item.y = pos["y"].as<float>() * TILE_SIZE;
}
}
// Counter
if (item_node["counter"]) {
item.counter = item_node["counter"].as<int>(0);
} else {
item.counter = 0;
}
// Colors (assigned from room defaults)
item.color1 = stringToColor(room.item_color1);
item.color2 = stringToColor(room.item_color2);
room.items.push_back(item);
}
if (verbose) {
std::cout << "Loaded " << room.items.size() << " items\n";
}
}
if (verbose) {
std::cout << "Room loaded successfully: " << FILE_NAME << '\n';
}
} catch (const YAML::Exception& e) {
std::cerr << "YAML parsing error in " << FILE_NAME << ": " << e.what() << '\n';
} catch (const std::exception& e) {
std::cerr << "Error loading room " << FILE_NAME << ": " << e.what() << '\n';
}
return room;
}

View File

@@ -12,7 +12,6 @@
#include <utility> // Para pair #include <utility> // Para pair
#include "core/resources/resource_cache.hpp" // Para Resource #include "core/resources/resource_cache.hpp" // Para Resource
#include "external/jail_audio.h" // Para JA_GetMusicState, JA_Music_state, JA_PlayMusic
// Calcula el cuadrado de la distancia entre dos puntos // Calcula el cuadrado de la distancia entre dos puntos
auto distanceSquared(int x1, int y1, int x2, int y2) -> double { auto distanceSquared(int x1, int y1, int x2, int y2) -> double {