From 6ea50cf35e477fd25964e5be26216e7a32fd59ab Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 16 Apr 2026 17:53:07 +0200 Subject: [PATCH] =?UTF-8?q?arreglos=20varios=20pa=20que=20JailDoc=20puga?= =?UTF-8?q?=20tastar=20com=20va=20a=C3=A7=C3=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 99 ++++++++++-- config/assets.yaml | 2 - data/keys/key1.gif | Bin 281 -> 660 bytes data/keys/key1.yaml | 4 +- data/room/900.yaml | 5 - data/room/902.yaml | 14 +- data/room/903.yaml | 6 + data/room/905.yaml | 34 ++--- data/zones/zones.yaml | 2 +- source/core/audio/jail_audio.hpp | 182 +++++++++++++++++------ source/core/resources/resource_cache.cpp | 1 + source/core/system/debug.cpp | 24 +-- source/game/defaults.hpp | 6 +- source/game/entities/key.cpp | 17 ++- source/game/entities/key.hpp | 4 + source/game/options.cpp | 20 +++ source/game/options.hpp | 6 + 17 files changed, 322 insertions(+), 104 deletions(-) diff --git a/.gitignore b/.gitignore index 03f0ac7..7f4aa97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,104 @@ -.cache/ -*data/config/config.yaml +# ─── OS: macOS ───────────────────────────────────────────── *.DS_Store +.AppleDouble +.LSOverride +._* +.Spotlight-V100 +.Trashes +.fseventsd +.DocumentRevisions-V100 +.TemporaryItems +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# ─── OS: Windows ─────────────────────────────────────────── thumbs.db Thumbs.db +ehthumbs.db +ehthumbs_vista.db desktop.ini +$RECYCLE.BIN/ +*.lnk + +# ─── OS: Linux ───────────────────────────────────────────── +*~ +.directory +.Trash-* +.nfs* + +# ─── Editores / IDEs ─────────────────────────────────────── +# Vim +*.swp +*.swo +*.swn +# Emacs +\#*\# +.\#* +# JetBrains +.idea/ +*.iml +# Sublime +*.sublime-workspace +*.sublime-project +# VSCode — settings.json del proyecto sí se versiona +.vscode/* +!.vscode/settings.json + +# ─── Claude Code ─────────────────────────────────────────── +.claude/settings.local.json +.cache/ + +# ─── Build / CMake ───────────────────────────────────────── +build/ +cmake-build-*/ +CMakeCache.txt +CMakeFiles/ +CMakeScripts/ +cmake_install.cmake +compile_commands.json +*.o +*.obj +*.a +*.lib +*.so +*.so.* +*.dylib +*.dll +*.d + +# ─── Ejecutables del juego ───────────────────────────────── *.exe *_macos *_linux -*.dmg -*.tar.gz -*.zip *.app *_debug* projecte_2026* -build/ -source/version.h -resources.pack +_projecte_2026 + +# ─── Tools generados ─────────────────────────────────────── tools/pack_resources/pack_resources tools/pack_resources/pack_resources.exe tools/pack_resources/pack_tool tools/pack_resources/pack_tool.exe +resources.pack + +# ─── Config de usuario (runtime) ─────────────────────────── +*data/config/config.yaml +source/version.h *.res + +# ─── Distribución ────────────────────────────────────────── +*.dmg +*.tar.gz +*.zip dist/ -.claude/settings.local.json -_projecte_2026 -screenshots/ \ No newline at end of file + +# ─── Logs / temporales ───────────────────────────────────── +*.log +*.tmp +*.bak +*.orig +*.rej + +# ─── Capturas ────────────────────────────────────────────── +screenshots/ diff --git a/config/assets.yaml b/config/assets.yaml index 06cf7eb..c2c67c4 100644 --- a/config/assets.yaml +++ b/config/assets.yaml @@ -210,10 +210,8 @@ assets: player: BITMAP: - ${PREFIX}/data/player/player.gif - - ${PREFIX}/data/player/player_game_over.gif ANIMATION: - ${PREFIX}/data/player/player.yaml - - ${PREFIX}/data/player/player_game_over.yaml # ITEMS items: diff --git a/data/keys/key1.gif b/data/keys/key1.gif index d5299d3fc2f73f202385cd2ba27d4b59da0aa0c8..c327e33856e03107fb2c56f68e761e8666a5dfb3 100644 GIT binary patch delta 473 zcmV;~0Ve*L0+a;@M@dFFH(}6`2giT^3r^fMknEjgCiw5{Qy}lzov_mzA4)okf3WcX~pj zg{6OqLS&=>rmC)PR)4QYtg)@No29a$T)MuzxxGYcbj4*6sk^JhLdMPj(5KJQ%0tIP zx6;?UR=zEL(c zo|K7FXHK4jJ5&CAsimUGE=GwWmB$k4%2`aCMddctPSL3PkU7Z;ZtGNfNWF?}!r>{i zutl$`UAlHn(uHmN#7)VhtXa5h-D>iLDHEudeLenN7#QSWi-kEJuC_@R@Ld3Q2kA}R z?(173m(N+=>?E@~CZ6Yk=1lf6=EbKwqdvE~RBKnSDI<{$FSc#fPRdr<&AYen-@t // Para strcpy, strlen #define STB_VORBIS_HEADER_ONLY -#include "external/stb_vorbis.h" // Para stb_vorbis_decode_memory +#include "external/stb_vorbis.h" // Para stb_vorbis_open_memory i streaming // --- Public Enums --- enum JA_Channel_state { JA_CHANNEL_INVALID, @@ -43,12 +43,16 @@ struct JA_Channel_t { struct JA_Music_t { SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000}; - Uint32 length{0}; - Uint8* buffer{nullptr}; + + // OGG comprimit en memòria. Propietat nostra; es copia des del fitxer una + // sola vegada en JA_LoadMusic i es descomprimix en chunks per streaming. + Uint8* ogg_data{nullptr}; + Uint32 ogg_length{0}; + stb_vorbis* vorbis{nullptr}; // Handle del decoder, viu tot el cicle del JA_Music_t + char* filename{nullptr}; - int pos{0}; - int times{0}; + int times{0}; // Loops restants (-1 = infinit, 0 = un sol play) SDL_AudioStream* stream{nullptr}; JA_Music_state state{JA_MUSIC_INVALID}; }; @@ -88,6 +92,73 @@ 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); inline void JA_CrossfadeMusic(JA_Music_t* music, int crossfade_ms, int loop = -1); +// --- Music streaming internals --- +// Bytes-per-sample per canal (sempre s16) +static constexpr int JA_MUSIC_BYTES_PER_SAMPLE = 2; +// Quants shorts decodifiquem per crida a get_samples_short_interleaved. +// 8192 shorts = 4096 samples/channel en estèreo ≈ 85ms de so a 48kHz. +static constexpr int JA_MUSIC_CHUNK_SHORTS = 8192; +// Umbral d'audio per davant del cursor de reproducció. Mantenim ≥ 0.5 s a +// l'SDL_AudioStream per absorbir jitter de frame i evitar underruns. +static constexpr float JA_MUSIC_LOW_WATER_SECONDS = 0.5f; + +// Decodifica un chunk del vorbis i el volca a l'stream. Retorna samples +// decodificats per canal (0 = EOF de l'stream vorbis). +inline int JA_FeedMusicChunk(JA_Music_t* music) { + if (!music || !music->vorbis || !music->stream) return 0; + + short chunk[JA_MUSIC_CHUNK_SHORTS]; + const int channels = music->spec.channels; + const int samples_per_channel = stb_vorbis_get_samples_short_interleaved( + music->vorbis, + channels, + chunk, + JA_MUSIC_CHUNK_SHORTS); + if (samples_per_channel <= 0) return 0; + + const int bytes = samples_per_channel * channels * JA_MUSIC_BYTES_PER_SAMPLE; + SDL_PutAudioStreamData(music->stream, chunk, bytes); + return samples_per_channel; +} + +// Reompli l'stream fins que tinga ≥ JA_MUSIC_LOW_WATER_SECONDS bufferats. +// En arribar a EOF del vorbis, aplica el loop (times) o deixa drenar. +inline void JA_PumpMusic(JA_Music_t* music) { + if (!music || !music->vorbis || !music->stream) return; + + const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE; + const int low_water_bytes = static_cast(JA_MUSIC_LOW_WATER_SECONDS * static_cast(bytes_per_second)); + + while (SDL_GetAudioStreamAvailable(music->stream) < low_water_bytes) { + const int decoded = JA_FeedMusicChunk(music); + if (decoded > 0) continue; + + // EOF: si queden loops, rebobinar; si no, tallar i deixar drenar. + if (music->times != 0) { + stb_vorbis_seek_start(music->vorbis); + if (music->times > 0) music->times--; + } else { + break; + } + } +} + +// Pre-carrega `duration_ms` de so dins l'stream actual abans que l'stream +// siga robat per outgoing_music (crossfade o fade-out). Imprescindible amb +// streaming: l'stream robat no es pot re-alimentar perquè perd la referència +// al seu vorbis decoder. No aplica loop — si el vorbis s'esgota abans, parem. +inline void JA_PreFillOutgoing(JA_Music_t* music, int duration_ms) { + if (!music || !music->vorbis || !music->stream) return; + + const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE; + const int needed_bytes = static_cast((static_cast(duration_ms) * bytes_per_second) / 1000); + + while (SDL_GetAudioStreamAvailable(music->stream) < needed_bytes) { + const int decoded = JA_FeedMusicChunk(music); + if (decoded <= 0) break; // EOF: deixem drenar el que hi haja + } +} + // --- Core Functions --- inline void JA_Update() { @@ -120,14 +191,11 @@ inline void JA_Update() { } } - // Buffering de musica en loop - 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(); + // Streaming: rellenem l'stream fins al low-water-mark i parem si el + // vorbis s'ha esgotat i no queden loops. + JA_PumpMusic(current_music); + if (current_music->times == 0 && SDL_GetAudioStreamAvailable(current_music->stream) == 0) { + JA_StopMusic(); } } @@ -172,19 +240,31 @@ inline void JA_Quit() { // --- Music Functions --- inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) { - JA_Music_t* music = new JA_Music_t(); + if (!buffer || length == 0) return nullptr; - int chan, samplerate; - short* output; - music->length = stb_vorbis_decode_memory(buffer, length, &chan, &samplerate, &output) * chan * 2; + // Còpia del OGG comprimit: stb_vorbis llig de forma persistent aquesta + // memòria mentre el handle estiga viu, així que hem de posseir-la nosaltres. + Uint8* ogg_copy = static_cast(SDL_malloc(length)); + if (!ogg_copy) return nullptr; + SDL_memcpy(ogg_copy, buffer, length); - music->spec.channels = chan; - music->spec.freq = samplerate; + int error = 0; + stb_vorbis* vorbis = stb_vorbis_open_memory(ogg_copy, static_cast(length), &error, nullptr); + if (!vorbis) { + SDL_free(ogg_copy); + SDL_Log("JA_LoadMusic: stb_vorbis_open_memory failed (error %d)", error); + return nullptr; + } + + auto* music = new JA_Music_t(); + music->ogg_data = ogg_copy; + music->ogg_length = length; + music->vorbis = vorbis; + + const stb_vorbis_info info = stb_vorbis_get_info(vorbis); + music->spec.channels = info.channels; + music->spec.freq = static_cast(info.sample_rate); music->spec.format = SDL_AUDIO_S16; - music->buffer = static_cast(SDL_malloc(music->length)); - SDL_memcpy(music->buffer, output, music->length); - free(output); - music->pos = 0; music->state = JA_MUSIC_STOPPED; return music; @@ -223,23 +303,29 @@ inline JA_Music_t* JA_LoadMusic(const char* filename) { } inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) { - if (!JA_musicEnabled || !music) return; // Añadida comprobación de music + if (!JA_musicEnabled || !music || !music->vorbis) return; JA_StopMusic(); current_music = music; - current_music->pos = 0; current_music->state = JA_MUSIC_PLAYING; current_music->times = loop; + // Rebobinem l'stream de vorbis al principi. Cobreix tant play-per-primera- + // vegada com replays/canvis de track que tornen a la mateixa pista. + stb_vorbis_seek_start(current_music->vorbis); + current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec); - if (!current_music->stream) { // Comprobar creación de stream + if (!current_music->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); + + // Pre-cargem el buffer abans de bindejar per evitar un underrun inicial. + JA_PumpMusic(current_music); + if (!SDL_BindAudioStream(sdlAudioDevice, current_music->stream)) printf("[ERROR] SDL_BindAudioStream failed!\n"); } @@ -276,12 +362,16 @@ 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; } + // Deixem el handle de vorbis viu — es tanca en JA_DeleteMusic. + // Rebobinem perquè un futur JA_PlayMusic comence des del principi. + if (current_music->vorbis) { + stb_vorbis_seek_start(current_music->vorbis); + } } inline void JA_FadeOutMusic(const int milliseconds) { @@ -294,19 +384,23 @@ inline void JA_FadeOutMusic(const int milliseconds) { outgoing_music.stream = nullptr; } + // Pre-omplim l'stream amb `milliseconds` de so: un cop robat, ja no + // tindrà accés al vorbis decoder i només podrà drenar el que tinga. + JA_PreFillOutgoing(current_music, milliseconds); + // Robar el stream del current_music al outgoing outgoing_music.stream = current_music->stream; outgoing_music.fade = {true, SDL_GetTicks(), milliseconds, JA_musicVolume}; // Dejar current_music sin stream (ya lo tiene outgoing) current_music->stream = nullptr; - current_music->pos = 0; current_music->state = JA_MUSIC_STOPPED; + if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis); incoming_fade.active = false; } inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const int loop) { - if (!JA_musicEnabled || !music) return; + if (!JA_musicEnabled || !music || !music->vorbis) return; // Destruir outgoing anterior si existe (crossfade durante crossfade) if (outgoing_music.stream) { @@ -315,28 +409,32 @@ inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const i outgoing_music.fade.active = false; } - // Robar el stream de la musica actual al outgoing para el fade-out + // Robar el stream de la musica actual al outgoing para el fade-out. + // Pre-omplim amb `crossfade_ms` de so perquè no es quede en silenci + // abans d'acabar el fade (l'stream robat ja no pot alimentar-se). if (current_music && current_music->state == JA_MUSIC_PLAYING && current_music->stream) { + JA_PreFillOutgoing(current_music, crossfade_ms); outgoing_music.stream = current_music->stream; outgoing_music.fade = {true, SDL_GetTicks(), crossfade_ms, JA_musicVolume}; current_music->stream = nullptr; current_music->state = JA_MUSIC_STOPPED; + if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis); } // Iniciar la nueva pista con gain=0 (el fade-in la sube gradualmente) current_music = music; - current_music->pos = 0; current_music->state = JA_MUSIC_PLAYING; current_music->times = loop; + stb_vorbis_seek_start(current_music->vorbis); current_music->stream = SDL_CreateAudioStream(¤t_music->spec, &JA_audioSpec); if (!current_music->stream) { SDL_Log("Failed to create audio stream for crossfade!"); current_music->state = JA_MUSIC_STOPPED; return; } - SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length); SDL_SetAudioStreamGain(current_music->stream, 0.0f); + JA_PumpMusic(current_music); // pre-carrega abans de bindejar SDL_BindAudioStream(sdlAudioDevice, current_music->stream); // Configurar fade-in @@ -356,8 +454,9 @@ inline void JA_DeleteMusic(JA_Music_t* music) { JA_StopMusic(); current_music = nullptr; } - SDL_free(music->buffer); if (music->stream) SDL_DestroyAudioStream(music->stream); + if (music->vorbis) stb_vorbis_close(music->vorbis); + SDL_free(music->ogg_data); free(music->filename); // filename se libera aquí delete music; } @@ -370,17 +469,14 @@ inline float JA_SetMusicVolume(float volume) { 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 void JA_SetMusicPosition(float /*value*/) { + // No implementat amb el backend de streaming. Mai va arribar a usar-se + // en el codi existent, així que es manté com a stub. } inline float JA_GetMusicPosition() { - if (!current_music) return 0; - return float(current_music->pos) / float(current_music->spec.freq); - // Nota: Ver `JA_SetMusicPosition` + // Veure nota a JA_SetMusicPosition. + return 0.0f; } inline void JA_EnableMusic(const bool value) { @@ -568,4 +664,4 @@ inline float JA_SetVolume(float volume) { float v = JA_SetMusicVolume(volume); JA_SetSoundVolume(v, -1); // Aplicar a todos los grupos de sonido return v; -} \ No newline at end of file +} diff --git a/source/core/resources/resource_cache.cpp b/source/core/resources/resource_cache.cpp index efafb0a..2311800 100644 --- a/source/core/resources/resource_cache.cpp +++ b/source/core/resources/resource_cache.cpp @@ -492,6 +492,7 @@ namespace Resource { // Muestra el progreso de carga void Cache::renderProgress() { + if (!Options::loading.show_progress) { return; } constexpr float X_PADDING = 60.0F; constexpr float Y_PADDING = 10.0F; constexpr float BAR_HEIGHT = 5.0F; diff --git a/source/core/system/debug.cpp b/source/core/system/debug.cpp index ee38f0b..1b13369 100644 --- a/source/core/system/debug.cpp +++ b/source/core/system/debug.cpp @@ -115,14 +115,14 @@ static auto sceneToString(SceneManager::Scene scene) -> std::string { // Carga la configuración de debug desde debug.yaml void Debug::loadFromFile() { - // Inicializar con valores de release por defecto - spawn_settings_.room = Defaults::Game::Room::INITIAL; - spawn_settings_.spawn_x = Defaults::Game::Player::SPAWN_X; - spawn_settings_.spawn_y = Defaults::Game::Player::SPAWN_Y; - spawn_settings_.flip = Defaults::Game::Player::SPAWN_FLIP; + // Inicializar con valores por defecto de debug + spawn_settings_.room = "902.yaml"; + spawn_settings_.spawn_x = 24 * Tile::SIZE; + spawn_settings_.spawn_y = 13 * Tile::SIZE; + spawn_settings_.flip = Flip::LEFT; initial_scene_ = SceneManager::Scene::GAME; lazy_loading_ = false; - render_info_enabled_ = false; + render_info_enabled_ = true; std::ifstream file(debug_file_path_); if (!file.good()) { @@ -166,14 +166,14 @@ void Debug::loadFromFile() { render_info_enabled_ = yaml["render_info"].get_value(); } } catch (...) { - // YAML inválido: resetear a defaults y sobreescribir - spawn_settings_.room = Defaults::Game::Room::INITIAL; - spawn_settings_.spawn_x = Defaults::Game::Player::SPAWN_X; - spawn_settings_.spawn_y = Defaults::Game::Player::SPAWN_Y; - spawn_settings_.flip = Defaults::Game::Player::SPAWN_FLIP; + // YAML inválido: resetear a defaults de debug y sobreescribir + spawn_settings_.room = "902.yaml"; + spawn_settings_.spawn_x = 24 * Tile::SIZE; + spawn_settings_.spawn_y = 13 * Tile::SIZE; + spawn_settings_.flip = Flip::LEFT; initial_scene_ = SceneManager::Scene::GAME; lazy_loading_ = false; - render_info_enabled_ = false; + render_info_enabled_ = true; saveToFile(); return; } diff --git a/source/game/defaults.hpp b/source/game/defaults.hpp index 77f91bc..99a9b46 100644 --- a/source/game/defaults.hpp +++ b/source/game/defaults.hpp @@ -18,7 +18,7 @@ namespace Defaults::Canvas { } // namespace Defaults::Canvas namespace Defaults::Window { - constexpr int ZOOM = 2; // Zoom de la ventana por defecto + constexpr int ZOOM = 3; // Zoom de la ventana por defecto } // namespace Defaults::Window namespace Defaults::Video { @@ -114,6 +114,10 @@ namespace Defaults::Console { constexpr Uint8 COMMAND_COLOR = 9; // Color del texto que escribe el usuario } // namespace Defaults::Console +namespace Defaults::Loading { + constexpr bool SHOW_PROGRESS = false; // Por defecto no mostrar la barra de carga de recursos +} // namespace Defaults::Loading + namespace Defaults::Localization { constexpr const char* LANGUAGE = "ca"; // Idioma por defecto (en = inglés, ca = catalán) } // namespace Defaults::Localization diff --git a/source/game/entities/key.cpp b/source/game/entities/key.cpp index 8f549a3..71d14f9 100644 --- a/source/game/entities/key.cpp +++ b/source/game/entities/key.cpp @@ -1,12 +1,15 @@ #include "game/entities/key.hpp" +#include // Para std::sin + #include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite #include "core/resources/resource_cache.hpp" // Para Resource // Constructor: carga la animación, posiciona el sprite y crea el collider Key::Key(const Data& data) : sprite_(std::make_shared(Resource::Cache::get()->getAnimationData(data.animation_path))), - id_(data.id) { + id_(data.id), + base_y_(data.y) { sprite_->setPosX(data.x); sprite_->setPosY(data.y); sprite_->setCurrentAnimation("default"); @@ -18,17 +21,22 @@ void Key::render() { sprite_->render(); } -// Avanza la animación de la llave +// Avanza la animación de la llave y aplica el movimiento flotante sinusoidal void Key::update(float delta_time) { if (is_paused_) { return; } sprite_->animate(delta_time); + + // Oscilacion sinusoidal sincronizada (mismo tiempo global para todas las llaves) + const float t = static_cast(SDL_GetTicks()) / 1000.0F; + const float offset = std::sin(t * 2.0F * SDL_PI_F / FLOAT_PERIOD_S) * FLOAT_AMPLITUDE; + sprite_->setPosY(base_y_ + offset); } -// Posición actual (para registrar pickup en KeyTracker) +// Posición base (el collider y el tracker usan la posición sin oscilación) auto Key::getPos() const -> SDL_FPoint { - return SDL_FPoint{.x = sprite_->getX(), .y = sprite_->getY()}; + return SDL_FPoint{.x = sprite_->getX(), .y = base_y_}; } #ifdef _DEBUG @@ -36,6 +44,7 @@ auto Key::getPos() const -> SDL_FPoint { void Key::setPosition(float x, float y) { sprite_->setPosX(x); sprite_->setPosY(y); + base_y_ = y; collider_ = sprite_->getRect(); } #endif diff --git a/source/game/entities/key.hpp b/source/game/entities/key.hpp index 61bbbea..837208d 100644 --- a/source/game/entities/key.hpp +++ b/source/game/entities/key.hpp @@ -24,6 +24,9 @@ class Key { float y{0.0F}; // Posición Y en píxeles }; + static constexpr float FLOAT_PERIOD_S = 2.0F; // Segundos por ciclo sinusoidal (todas las llaves sincronizadas) + static constexpr float FLOAT_AMPLITUDE = 2.0F; // Amplitud en píxeles + explicit Key(const Data& data); ~Key() = default; @@ -44,5 +47,6 @@ class Key { std::shared_ptr sprite_; // Sprite animado de la llave SDL_FRect collider_{}; // Rectángulo de colisión std::string id_; // Identificador + float base_y_{0.0F}; // Posición Y base (el sprite oscila alrededor) bool is_paused_{false}; // Indica si la llave está pausada }; diff --git a/source/game/options.cpp b/source/game/options.cpp index bc9863d..566eead 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -631,6 +631,19 @@ namespace Options { } } + // Carga configuración de la pantalla de carga de recursos desde YAML + void loadLoadingConfigFromYaml(const fkyaml::node& yaml) { + if (!yaml.contains("loading")) { return; } + const auto& l = yaml["loading"]; + + if (l.contains("show_progress")) { + try { + loading.show_progress = l["show_progress"].get_value(); + } catch (...) { /* @INTENTIONAL: campo yaml ausente o malformado → dejar default */ + } + } + } + // Carga configuración de idioma desde YAML void loadLocalizationFromYaml(const fkyaml::node& yaml) { if (yaml.contains("localization")) { @@ -696,6 +709,7 @@ namespace Options { loadKioskConfigFromYaml(yaml); loadLocalizationFromYaml(yaml); loadConsoleConfigFromYaml(yaml); + loadLoadingConfigFromYaml(yaml); std::cout << "Config file loaded successfully\n\n"; @@ -832,6 +846,12 @@ namespace Options { file << " text: \"" << kiosk.text << "\"\n"; file << " infinite_lives: " << (kiosk.infinite_lives ? "true" : "false") << "\n"; + // LOADING + file << "\n"; + file << "# LOADING (pantalla de carga de recursos)\n"; + file << "loading:\n"; + file << " show_progress: " << (loading.show_progress ? "true" : "false") << " # mostrar barra de progreso al cargar recursos\n"; + // LOCALIZATION file << "\n"; file << "# LOCALIZATION (en = English, ca = Catalan)\n"; diff --git a/source/game/options.hpp b/source/game/options.hpp index 0956389..991cc08 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -138,6 +138,11 @@ namespace Options { float height{Defaults::Canvas::HEIGHT}; // Alto de la resolucion del juego }; + // Estructura para las opciones de la pantalla de carga de recursos + struct Loading { + bool show_progress{Defaults::Loading::SHOW_PROGRESS}; // Mostrar barra de progreso al cargar recursos + }; + // Estructura para las opciones visuales de la consola en pantalla struct Console { bool transparent{Defaults::Console::TRANSPARENT}; // true = sin fondo, false = sólida @@ -191,6 +196,7 @@ namespace Options { inline GamepadControls gamepad_controls{}; // Botones del gamepad usados para jugar inline Kiosk kiosk{}; // Opciones del modo kiosko inline Console console{}; // Opciones visuales de la consola en pantalla + inline Loading loading{}; // Opciones de la pantalla de carga de recursos // Idioma del juego (establecido al inicio, sin cambio en caliente) inline std::string language{Defaults::Localization::LANGUAGE};