tidy-fix automàtic (sense naming)

This commit is contained in:
2026-05-14 18:28:23 +02:00
parent 358e91ea30
commit b7a551c158
81 changed files with 1549 additions and 831 deletions
+9 -3
View File
@@ -62,10 +62,14 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa
return;
}
if (!music_enabled_) return;
if (!music_enabled_) {
return;
}
auto* resource = AudioResource::getMusic(name);
if (resource == nullptr) return;
if (resource == nullptr) {
return;
}
if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) {
JA_CrossfadeMusic(resource, crossfade_ms, loop);
@@ -83,7 +87,9 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa
// Reproduce la música por puntero (con crossfade opcional)
void Audio::playMusic(JA_Music_t* music, const int loop, const int crossfade_ms) {
if (!music_enabled_ || music == nullptr) return;
if (!music_enabled_ || music == nullptr) {
return;
}
if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) {
JA_CrossfadeMusic(music, crossfade_ms, loop);
+1 -1
View File
@@ -62,7 +62,7 @@ class Audio {
// --- Helpers de conversió per a la capa de presentació ---
// UI (menús, notificacions) manega enters 0..100; internament viu float 0..1.
static constexpr auto toPercent(float volume) -> int {
return static_cast<int>(volume * 100.0F + 0.5F);
return static_cast<int>((volume * 100.0F) + 0.5F);
}
static constexpr auto fromPercent(int percent) -> float {
return static_cast<float>(percent) / 100.0F;
+2 -2
View File
@@ -4,11 +4,11 @@
namespace AudioResource {
JA_Music_t* getMusic(const std::string& name) {
auto getMusic(const std::string& name) -> JA_Music_t* {
return Resource::Cache::get()->getMusic(name);
}
JA_Sound_t* getSound(const std::string& name) {
auto getSound(const std::string& name) -> JA_Sound_t* {
return Resource::Cache::get()->getSound(name);
}
+2 -2
View File
@@ -12,6 +12,6 @@ struct JA_Music_t;
struct JA_Sound_t;
namespace AudioResource {
JA_Music_t* getMusic(const std::string& name);
JA_Sound_t* getSound(const std::string& name);
auto getMusic(const std::string& name) -> JA_Music_t*;
auto getSound(const std::string& name) -> JA_Sound_t*;
} // namespace AudioResource
+221 -120
View File
@@ -2,10 +2,10 @@
// --- Includes ---
#include <SDL3/SDL.h>
#include <stdint.h> // Para uint32_t, uint8_t
#include <stdio.h> // Para NULL, fseek, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
#include <stdlib.h> // Para free, malloc
#include <cstdint> // Para uint32_t, uint8_t
#include <cstdio> // Para NULL, fseek, fclose, fopen, fread, ftell, FILE, SEEK_END, SEEK_SET
#include <cstdlib> // Para free, malloc
#include <iostream> // Para std::cout
#include <memory> // Para std::unique_ptr
#include <string> // Para std::string
@@ -19,7 +19,9 @@
// overhead gràcies a EBO, igual que un unique_ptr amb default_delete.
struct SDLFreeDeleter {
void operator()(Uint8* p) const noexcept {
if (p) SDL_free(p);
if (p != nullptr) {
SDL_free(p);
}
}
};
@@ -40,8 +42,10 @@ enum JA_Music_state {
};
// --- Struct Definitions ---
#define JA_MAX_SIMULTANEOUS_CHANNELS 20
#define JA_MAX_GROUPS 2
enum {
JA_MAX_SIMULTANEOUS_CHANNELS = 20,
JA_MAX_GROUPS = 2
};
struct JA_Sound_t {
SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000};
@@ -84,7 +88,7 @@ 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_musicVolume{1.0F};
inline float JA_soundVolume[JA_MAX_GROUPS];
inline bool JA_musicEnabled{true};
inline bool JA_soundEnabled{true};
@@ -95,7 +99,7 @@ struct JA_FadeState {
bool active{false};
Uint64 start_time{0};
int duration_ms{0};
float initial_volume{0.0f};
float initial_volume{0.0F};
};
struct JA_OutgoingMusic {
@@ -108,8 +112,8 @@ inline JA_FadeState incoming_fade;
// --- 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);
inline void JA_StopChannel(int channel);
inline auto JA_PlaySoundOnChannel(JA_Sound_t* sound, int channel, int loop = 0, int group = 0) -> int;
inline void JA_CrossfadeMusic(JA_Music_t* music, int crossfade_ms, int loop = -1);
// --- Music streaming internals ---
@@ -120,12 +124,14 @@ static constexpr int JA_MUSIC_BYTES_PER_SAMPLE = 2;
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;
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;
inline auto JA_FeedMusicChunk(JA_Music_t* music) -> int {
if ((music == nullptr) || (music->vorbis == nullptr) || (music->stream == nullptr)) {
return 0;
}
short chunk[JA_MUSIC_CHUNK_SHORTS];
const int num_channels = music->spec.channels;
@@ -134,7 +140,9 @@ inline int JA_FeedMusicChunk(JA_Music_t* music) {
num_channels,
chunk,
JA_MUSIC_CHUNK_SHORTS);
if (samples_per_channel <= 0) return 0;
if (samples_per_channel <= 0) {
return 0;
}
const int bytes = samples_per_channel * num_channels * JA_MUSIC_BYTES_PER_SAMPLE;
SDL_PutAudioStreamData(music->stream, chunk, bytes);
@@ -144,19 +152,25 @@ inline int JA_FeedMusicChunk(JA_Music_t* music) {
// 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;
if ((music == nullptr) || (music->vorbis == nullptr) || (music->stream == nullptr)) {
return;
}
const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE;
const int low_water_bytes = static_cast<int>(JA_MUSIC_LOW_WATER_SECONDS * static_cast<float>(bytes_per_second));
while (SDL_GetAudioStreamAvailable(music->stream) < low_water_bytes) {
const int decoded = JA_FeedMusicChunk(music);
if (decoded > 0) continue;
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--;
if (music->times > 0) {
music->times--;
}
} else {
break;
}
@@ -168,14 +182,18 @@ inline void JA_PumpMusic(JA_Music_t* music) {
// 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;
if ((music == nullptr) || (music->vorbis == nullptr) || (music->stream == nullptr)) {
return;
}
const int bytes_per_second = music->spec.freq * music->spec.channels * JA_MUSIC_BYTES_PER_SAMPLE;
const int needed_bytes = static_cast<int>((static_cast<int64_t>(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
if (decoded <= 0) {
break; // EOF: deixem drenar el que hi haja
}
}
}
@@ -183,7 +201,7 @@ inline void JA_PreFillOutgoing(JA_Music_t* music, int duration_ms) {
inline void JA_Update() {
// --- Outgoing music fade-out (crossfade o fade-out a silencio) ---
if (outgoing_music.stream && outgoing_music.fade.active) {
if ((outgoing_music.stream != nullptr) && outgoing_music.fade.active) {
Uint64 now = SDL_GetTicks();
Uint64 elapsed = now - outgoing_music.fade.start_time;
if (elapsed >= (Uint64)outgoing_music.fade.duration_ms) {
@@ -192,12 +210,12 @@ inline void JA_Update() {
outgoing_music.fade.active = false;
} else {
float percent = (float)elapsed / (float)outgoing_music.fade.duration_ms;
SDL_SetAudioStreamGain(outgoing_music.stream, outgoing_music.fade.initial_volume * (1.0f - percent));
SDL_SetAudioStreamGain(outgoing_music.stream, outgoing_music.fade.initial_volume * (1.0F - percent));
}
}
// --- Current music ---
if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING) {
if (JA_musicEnabled && (current_music != nullptr) && current_music->state == JA_MUSIC_PLAYING) {
// Fade-in (parte de un crossfade)
if (incoming_fade.active) {
Uint64 now = SDL_GetTicks();
@@ -221,42 +239,59 @@ inline void JA_Update() {
// --- Sound channels ---
if (JA_soundEnabled) {
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i)
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) {
if (channels[i].state == JA_CHANNEL_PLAYING) {
if (channels[i].times != 0) {
if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) {
SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer.get(), channels[i].sound->length);
if (channels[i].times > 0) channels[i].times--;
if (channels[i].times > 0) {
channels[i].times--;
}
}
} else {
if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i);
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) {
JA_audioSpec = {format, num_channels, freq};
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
JA_audioSpec = {.format = format, .channels = num_channels, .freq = freq};
if (sdlAudioDevice != 0u) {
SDL_CloseAudioDevice(sdlAudioDevice);
}
sdlAudioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &JA_audioSpec);
if (sdlAudioDevice == 0) std::cout << "Failed to initialize SDL audio!" << '\n';
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;
if (sdlAudioDevice == 0) {
std::cout << "Failed to initialize SDL audio!" << '\n';
}
for (auto& channel : channels) {
channel.state = JA_CHANNEL_FREE;
}
for (float& i : JA_soundVolume) {
i = 0.5F;
}
}
inline void JA_Quit() {
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
}
if (sdlAudioDevice) SDL_CloseAudioDevice(sdlAudioDevice);
if (sdlAudioDevice != 0u) {
SDL_CloseAudioDevice(sdlAudioDevice);
}
sdlAudioDevice = 0;
}
// --- Music Functions ---
inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
if (!buffer || length == 0) return nullptr;
inline auto JA_LoadMusic(const Uint8* buffer, Uint32 length) -> JA_Music_t* {
if ((buffer == nullptr) || length == 0) {
return nullptr;
}
// Allocem el JA_Music_t primer per aprofitar el seu `std::vector<Uint8>`
// com a propietari del OGG comprimit. stb_vorbis guarda un punter
@@ -270,7 +305,7 @@ inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
static_cast<int>(length),
&vorbis_error,
nullptr);
if (!music->vorbis) {
if (music->vorbis == nullptr) {
std::cout << "JA_LoadMusic: stb_vorbis_open_memory failed (error " << vorbis_error << ")" << '\n';
delete music;
return nullptr;
@@ -287,21 +322,25 @@ inline JA_Music_t* JA_LoadMusic(const Uint8* buffer, Uint32 length) {
// Overload amb filename — els callers l'usen per poder comparar la música
// en curs amb JA_GetMusicFilename() i no rearrancar-la si ja és la mateixa.
inline JA_Music_t* JA_LoadMusic(Uint8* buffer, Uint32 length, const char* filename) {
inline auto JA_LoadMusic(Uint8* buffer, Uint32 length, const char* filename) -> JA_Music_t* {
JA_Music_t* music = JA_LoadMusic(static_cast<const Uint8*>(buffer), length);
if (music && filename) music->filename = filename;
if ((music != nullptr) && (filename != nullptr)) {
music->filename = filename;
}
return music;
}
inline JA_Music_t* JA_LoadMusic(const char* filename) {
inline auto JA_LoadMusic(const char* filename) -> JA_Music_t* {
// Carreguem primer el arxiu en memòria i després el descomprimim.
FILE* f = fopen(filename, "rb");
if (!f) return nullptr;
if (f == nullptr) {
return nullptr;
}
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
auto* buffer = static_cast<Uint8*>(malloc(fsize + 1));
if (!buffer) {
if (buffer == nullptr) {
fclose(f);
return nullptr;
}
@@ -313,7 +352,7 @@ inline JA_Music_t* JA_LoadMusic(const char* filename) {
fclose(f);
JA_Music_t* music = JA_LoadMusic(static_cast<const Uint8*>(buffer), static_cast<Uint32>(fsize));
if (music) {
if (music != nullptr) {
music->filename = filename;
}
@@ -323,7 +362,9 @@ 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 || !music->vorbis) return;
if (!JA_musicEnabled || (music == nullptr) || (music->vorbis == nullptr)) {
return;
}
JA_StopMusic();
@@ -336,7 +377,7 @@ inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) {
stb_vorbis_seek_start(current_music->vorbis);
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &JA_audioSpec);
if (!current_music->stream) {
if (current_music->stream == nullptr) {
std::cout << "Failed to create audio stream!" << '\n';
current_music->state = JA_MUSIC_STOPPED;
return;
@@ -351,23 +392,35 @@ inline void JA_PlayMusic(JA_Music_t* music, const int loop = -1) {
}
}
inline const char* JA_GetMusicFilename(const JA_Music_t* music = nullptr) {
if (!music) music = current_music;
if (!music || music->filename.empty()) return nullptr;
inline auto JA_GetMusicFilename(const JA_Music_t* music = nullptr) -> const char* {
if (music == nullptr) {
music = current_music;
}
if ((music == nullptr) || music->filename.empty()) {
return nullptr;
}
return music->filename.c_str();
}
inline void JA_PauseMusic() {
if (!JA_musicEnabled) return;
if (!current_music || current_music->state != JA_MUSIC_PLAYING) return;
if (!JA_musicEnabled) {
return;
}
if ((current_music == nullptr) || current_music->state != JA_MUSIC_PLAYING) {
return;
}
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;
if (!JA_musicEnabled) {
return;
}
if ((current_music == nullptr) || current_music->state != JA_MUSIC_PAUSED) {
return;
}
current_music->state = JA_MUSIC_PLAYING;
SDL_BindAudioStream(sdlAudioDevice, current_music->stream);
@@ -375,33 +428,39 @@ inline void JA_ResumeMusic() {
inline void JA_StopMusic() {
// Limpiar outgoing crossfade si existe
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
outgoing_music.fade.active = false;
}
incoming_fade.active = false;
if (!current_music || current_music->state == JA_MUSIC_INVALID || current_music->state == JA_MUSIC_STOPPED) return;
if ((current_music == nullptr) || current_music->state == JA_MUSIC_INVALID || current_music->state == JA_MUSIC_STOPPED) {
return;
}
current_music->state = JA_MUSIC_STOPPED;
if (current_music->stream) {
if (current_music->stream != nullptr) {
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) {
if (current_music->vorbis != nullptr) {
stb_vorbis_seek_start(current_music->vorbis);
}
}
inline void JA_FadeOutMusic(const int milliseconds) {
if (!JA_musicEnabled) return;
if (!current_music || current_music->state != JA_MUSIC_PLAYING) return;
if (!JA_musicEnabled) {
return;
}
if ((current_music == nullptr) || current_music->state != JA_MUSIC_PLAYING) {
return;
}
// Destruir outgoing anterior si existe
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
}
@@ -412,20 +471,24 @@ inline void JA_FadeOutMusic(const int milliseconds) {
// Robar el stream del current_music al outgoing
outgoing_music.stream = current_music->stream;
outgoing_music.fade = {true, SDL_GetTicks(), milliseconds, JA_musicVolume};
outgoing_music.fade = {.active = true, .start_time = SDL_GetTicks(), .duration_ms = milliseconds, .initial_volume = JA_musicVolume};
// Dejar current_music sin stream (ya lo tiene outgoing)
current_music->stream = nullptr;
current_music->state = JA_MUSIC_STOPPED;
if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis);
if (current_music->vorbis != nullptr) {
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 || !music->vorbis) return;
if (!JA_musicEnabled || (music == nullptr) || (music->vorbis == nullptr)) {
return;
}
// Destruir outgoing anterior si existe (crossfade durante crossfade)
if (outgoing_music.stream) {
if (outgoing_music.stream != nullptr) {
SDL_DestroyAudioStream(outgoing_music.stream);
outgoing_music.stream = nullptr;
outgoing_music.fade.active = false;
@@ -434,13 +497,15 @@ inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const i
// 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) {
if ((current_music != nullptr) && current_music->state == JA_MUSIC_PLAYING && (current_music->stream != nullptr)) {
JA_PreFillOutgoing(current_music, crossfade_ms);
outgoing_music.stream = current_music->stream;
outgoing_music.fade = {true, SDL_GetTicks(), crossfade_ms, JA_musicVolume};
outgoing_music.fade = {.active = true, .start_time = SDL_GetTicks(), .duration_ms = crossfade_ms, .initial_volume = JA_musicVolume};
current_music->stream = nullptr;
current_music->state = JA_MUSIC_STOPPED;
if (current_music->vorbis) stb_vorbis_seek_start(current_music->vorbis);
if (current_music->vorbis != nullptr) {
stb_vorbis_seek_start(current_music->vorbis);
}
}
// Iniciar la nueva pista con gain=0 (el fade-in la sube gradualmente)
@@ -450,42 +515,52 @@ inline void JA_CrossfadeMusic(JA_Music_t* music, const int crossfade_ms, const i
stb_vorbis_seek_start(current_music->vorbis);
current_music->stream = SDL_CreateAudioStream(&current_music->spec, &JA_audioSpec);
if (!current_music->stream) {
if (current_music->stream == nullptr) {
std::cout << "Failed to create audio stream for crossfade!" << '\n';
current_music->state = JA_MUSIC_STOPPED;
return;
}
SDL_SetAudioStreamGain(current_music->stream, 0.0f);
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
incoming_fade = {true, SDL_GetTicks(), crossfade_ms, 0.0f};
incoming_fade = {.active = true, .start_time = SDL_GetTicks(), .duration_ms = crossfade_ms, .initial_volume = 0.0F};
}
inline JA_Music_state JA_GetMusicState() {
if (!JA_musicEnabled) return JA_MUSIC_DISABLED;
if (!current_music) return JA_MUSIC_INVALID;
inline auto JA_GetMusicState() -> JA_Music_state {
if (!JA_musicEnabled) {
return JA_MUSIC_DISABLED;
}
if (current_music == nullptr) {
return JA_MUSIC_INVALID;
}
return current_music->state;
}
inline void JA_DeleteMusic(JA_Music_t* music) {
if (!music) return;
if (music == nullptr) {
return;
}
if (current_music == music) {
JA_StopMusic();
current_music = nullptr;
}
if (music->stream) SDL_DestroyAudioStream(music->stream);
if (music->vorbis) stb_vorbis_close(music->vorbis);
if (music->stream != nullptr) {
SDL_DestroyAudioStream(music->stream);
}
if (music->vorbis != nullptr) {
stb_vorbis_close(music->vorbis);
}
// ogg_data (std::vector) i filename (std::string) s'alliberen sols
// al destructor de JA_Music_t.
delete music;
}
inline float JA_SetMusicVolume(float volume) {
JA_musicVolume = SDL_clamp(volume, 0.0f, 1.0f);
if (current_music && current_music->stream) {
inline auto JA_SetMusicVolume(float volume) -> float {
JA_musicVolume = SDL_clamp(volume, 0.0F, 1.0F);
if ((current_music != nullptr) && (current_music->stream != nullptr)) {
SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume);
}
return JA_musicVolume;
@@ -495,18 +570,20 @@ inline void JA_SetMusicPosition(float /*value*/) {
// No implementat amb el backend de streaming.
}
inline float JA_GetMusicPosition() {
return 0.0f;
inline auto JA_GetMusicPosition() -> float {
return 0.0F;
}
inline void JA_EnableMusic(const bool value) {
if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING)) JA_StopMusic();
if (!value && (current_music != nullptr) && (current_music->state == JA_MUSIC_PLAYING)) {
JA_StopMusic();
}
JA_musicEnabled = value;
}
// --- Sound Functions ---
inline JA_Sound_t* JA_LoadSound(uint8_t* buffer, uint32_t size) {
inline auto JA_LoadSound(uint8_t* buffer, uint32_t size) -> JA_Sound_t* {
auto sound = std::make_unique<JA_Sound_t>();
Uint8* raw = nullptr;
if (!SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &raw, &sound->length)) {
@@ -517,7 +594,7 @@ inline JA_Sound_t* JA_LoadSound(uint8_t* buffer, uint32_t size) {
return sound.release();
}
inline JA_Sound_t* JA_LoadSound(const char* filename) {
inline auto JA_LoadSound(const char* filename) -> JA_Sound_t* {
auto sound = std::make_unique<JA_Sound_t>();
Uint8* raw = nullptr;
if (!SDL_LoadWAV(filename, &sound->spec, &raw, &sound->length)) {
@@ -528,8 +605,10 @@ inline JA_Sound_t* JA_LoadSound(const char* filename) {
return sound.release();
}
inline int JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group = 0) {
if (!JA_soundEnabled || !sound) return -1;
inline auto JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group = 0) -> int {
if (!JA_soundEnabled || (sound == nullptr)) {
return -1;
}
int channel = 0;
while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; }
@@ -541,9 +620,13 @@ inline int JA_PlaySound(JA_Sound_t* sound, const int loop = 0, const int group =
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;
inline auto JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int loop, const int group) -> int {
if (!JA_soundEnabled || (sound == nullptr)) {
return -1;
}
if (channel < 0 || channel >= JA_MAX_SIMULTANEOUS_CHANNELS) {
return -1;
}
JA_StopChannel(channel);
@@ -554,7 +637,7 @@ inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int
channels[channel].state = JA_CHANNEL_PLAYING;
channels[channel].stream = SDL_CreateAudioStream(&channels[channel].sound->spec, &JA_audioSpec);
if (!channels[channel].stream) {
if (channels[channel].stream == nullptr) {
std::cout << "Failed to create audio stream for sound!" << '\n';
channels[channel].state = JA_CHANNEL_FREE;
return -1;
@@ -568,23 +651,30 @@ inline int JA_PlaySoundOnChannel(JA_Sound_t* sound, const int channel, const int
}
inline void JA_DeleteSound(JA_Sound_t* sound) {
if (!sound) return;
if (sound == nullptr) {
return;
}
for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) {
if (channels[i].sound == sound) JA_StopChannel(i);
if (channels[i].sound == sound) {
JA_StopChannel(i);
}
}
// buffer es destrueix automàticament via RAII (SDLFreeDeleter).
delete sound;
}
inline void JA_PauseChannel(const int channel) {
if (!JA_soundEnabled) return;
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);
for (auto& channel : channels) {
if (channel.state == JA_CHANNEL_PLAYING) {
channel.state = JA_CHANNEL_PAUSED;
SDL_UnbindAudioStream(channel.stream);
}
}
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state == JA_CHANNEL_PLAYING) {
channels[channel].state = JA_CHANNEL_PAUSED;
@@ -594,14 +684,17 @@ inline void JA_PauseChannel(const int channel) {
}
inline void JA_ResumeChannel(const int channel) {
if (!JA_soundEnabled) return;
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);
for (auto& channel : channels) {
if (channel.state == JA_CHANNEL_PAUSED) {
channel.state = JA_CHANNEL_PLAYING;
SDL_BindAudioStream(sdlAudioDevice, channel.stream);
}
}
} else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) {
if (channels[channel].state == JA_CHANNEL_PAUSED) {
channels[channel].state = JA_CHANNEL_PLAYING;
@@ -612,18 +705,22 @@ inline void JA_ResumeChannel(const int channel) {
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 = nullptr;
for (auto& channel : channels) {
if (channel.state != JA_CHANNEL_FREE) {
if (channel.stream != nullptr) {
SDL_DestroyAudioStream(channel.stream);
}
channel.stream = nullptr;
channel.state = JA_CHANNEL_FREE;
channel.pos = 0;
channel.sound = nullptr;
}
}
} 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);
if (channels[channel].stream != nullptr) {
SDL_DestroyAudioStream(channels[channel].stream);
}
channels[channel].stream = nullptr;
channels[channel].state = JA_CHANNEL_FREE;
channels[channel].pos = 0;
@@ -632,19 +729,23 @@ inline void JA_StopChannel(const int channel) {
}
}
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;
inline auto JA_GetChannelState(const int channel) -> JA_Channel_state {
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) {
const float v = SDL_clamp(volume, 0.0f, 1.0f);
inline auto JA_SetSoundVolume(float volume, const int group = -1) -> float {
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;
for (float& i : JA_soundVolume) {
i = v;
}
} else if (group >= 0 && group < JA_MAX_GROUPS) {
JA_soundVolume[group] = v;
@@ -653,11 +754,11 @@ inline float JA_SetSoundVolume(float volume, const int group = -1) {
}
// Aplicar volum als canals actius.
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]);
for (auto& channel : channels) {
if ((channel.state == JA_CHANNEL_PLAYING) || (channel.state == JA_CHANNEL_PAUSED)) {
if (group == -1 || channel.group == group) {
if (channel.stream != nullptr) {
SDL_SetAudioStreamGain(channel.stream, JA_soundVolume[channel.group]);
}
}
}
@@ -672,7 +773,7 @@ inline void JA_EnableSound(const bool value) {
JA_soundEnabled = value;
}
inline float JA_SetVolume(float volume) {
inline auto JA_SetVolume(float volume) -> float {
float v = JA_SetMusicVolume(volume);
JA_SetSoundVolume(v, -1);
return v;
+44 -20
View File
@@ -52,8 +52,8 @@ namespace Gamepad {
// Recorta el nom visible del mando: trim des del primer '(' o '['
// (per a evitar coses com "Retroid Controller (vendor: 1001) ..."),
// elimina espais finals i talla a 25 caràcters.
static std::string prettyName(const char* raw) {
std::string name = (raw && *raw) ? raw : "Gamepad";
static auto prettyName(const char* raw) -> std::string {
std::string name = ((raw != nullptr) && (*raw != 0)) ? raw : "Gamepad";
const auto pos = name.find_first_of("([");
if (pos != std::string::npos) {
name.erase(pos);
@@ -64,7 +64,9 @@ namespace Gamepad {
if (name.size() > 25) {
name.resize(25);
}
if (name.empty()) name = "Gamepad";
if (name.empty()) {
name = "Gamepad";
}
return name;
}
@@ -98,12 +100,14 @@ namespace Gamepad {
static void openFirstGamepad() {
int count = 0;
SDL_JoystickID* ids = SDL_GetJoysticks(&count);
if (ids) {
if (ids != nullptr) {
for (int i = 0; i < count; ++i) {
installWebStandardMapping(ids[i]);
if (!SDL_IsGamepad(ids[i])) continue;
if (!SDL_IsGamepad(ids[i])) {
continue;
}
pad_ = SDL_OpenGamepad(ids[i]);
if (pad_) {
if (pad_ != nullptr) {
pad_id_ = ids[i];
SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_));
break;
@@ -128,7 +132,7 @@ namespace Gamepad {
}
void destroy() {
if (pad_) {
if (pad_ != nullptr) {
SDL_CloseGamepad(pad_);
pad_ = nullptr;
pad_id_ = 0;
@@ -145,12 +149,14 @@ namespace Gamepad {
// GAMEPAD_ADDED) perquè SDL no reconeix el GUID. Escoltem els dos i
// injectem el mapping estàndard abans d'obrir el mando.
if (event.type == SDL_EVENT_GAMEPAD_ADDED || event.type == SDL_EVENT_JOYSTICK_ADDED) {
if (!pad_) {
if (pad_ == nullptr) {
SDL_JoystickID jid = event.jdevice.which;
installWebStandardMapping(jid);
if (!SDL_IsGamepad(jid)) return;
if (!SDL_IsGamepad(jid)) {
return;
}
pad_ = SDL_OpenGamepad(jid);
if (pad_) {
if (pad_ != nullptr) {
pad_id_ = jid;
std::string name = prettyName(SDL_GetGamepadName(pad_));
SDL_Log("Gamepad connectat: %s", name.c_str());
@@ -158,7 +164,7 @@ namespace Gamepad {
}
}
} else if (event.type == SDL_EVENT_GAMEPAD_REMOVED || event.type == SDL_EVENT_JOYSTICK_REMOVED) {
if (pad_ && event.jdevice.which == pad_id_) {
if ((pad_ != nullptr) && event.jdevice.which == pad_id_) {
std::string saved_name = prettyName(SDL_GetGamepadName(pad_));
SDL_Log("Gamepad desconnectat: %s", saved_name.c_str());
SDL_CloseGamepad(pad_);
@@ -190,7 +196,9 @@ namespace Gamepad {
}
void update() {
if (!pad_) return;
if (pad_ == nullptr) {
return;
}
// D-pad
bool dup = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_DPAD_UP);
@@ -220,19 +228,35 @@ namespace Gamepad {
bool back = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_BACK);
// Select (Back) → obre/tanca menú de servei (flanc)
if (back && !prev_back_) pushKey(KeyConfig::scancode("menu_toggle"));
if (back && !prev_back_) {
pushKey(KeyConfig::scancode("menu_toggle"));
}
// Start → pausa (flanc)
if (start && !prev_start_) pushKey(KeyConfig::scancode("pause_toggle"));
if (start && !prev_start_) {
pushKey(KeyConfig::scancode("pause_toggle"));
}
if (Menu::isOpen()) {
// Navegació del menú per flanc
if (up && !prev_up_) pushKey(SDL_SCANCODE_UP);
if (dn && !prev_down_) pushKey(SDL_SCANCODE_DOWN);
if (lt && !prev_left_) pushKey(SDL_SCANCODE_LEFT);
if (rt && !prev_right_) pushKey(SDL_SCANCODE_RIGHT);
if (up && !prev_up_) {
pushKey(SDL_SCANCODE_UP);
}
if (dn && !prev_down_) {
pushKey(SDL_SCANCODE_DOWN);
}
if (lt && !prev_left_) {
pushKey(SDL_SCANCODE_LEFT);
}
if (rt && !prev_right_) {
pushKey(SDL_SCANCODE_RIGHT);
}
// EAST accepta, SOUTH cancela / endarrere
if (east && !prev_east_) pushKey(SDL_SCANCODE_RETURN);
if (south && !prev_south_) pushKey(SDL_SCANCODE_BACKSPACE);
if (east && !prev_east_) {
pushKey(SDL_SCANCODE_RETURN);
}
if (south && !prev_south_) {
pushKey(SDL_SCANCODE_BACKSPACE);
}
// Assegura que el joc no rep tecles de moviment mentre el menú està obert
JI_SetVirtualKey(SDL_SCANCODE_UP, JI_VSRC_GAMEPAD, false);
+30 -10
View File
@@ -34,7 +34,9 @@ namespace GlobalInputs {
snprintf(msg, sizeof(msg), Locale::get("notifications.zoom_fmt"), Screen::get()->getZoom());
Overlay::showNotification(msg);
}
if (dec_zoom) consumed = true;
if (dec_zoom) {
consumed = true;
}
dec_zoom_prev = dec_zoom;
// F2 — Augmentar zoom
@@ -45,7 +47,9 @@ namespace GlobalInputs {
snprintf(msg, sizeof(msg), Locale::get("notifications.zoom_fmt"), Screen::get()->getZoom());
Overlay::showNotification(msg);
}
if (inc_zoom) consumed = true;
if (inc_zoom) {
consumed = true;
}
inc_zoom_prev = inc_zoom;
// F3 — Toggle pantalla completa
@@ -54,7 +58,9 @@ namespace GlobalInputs {
Screen::get()->toggleFullscreen();
Overlay::showNotification(Screen::get()->isFullscreen() ? Locale::get("notifications.fullscreen") : Locale::get("notifications.windowed"));
}
if (fullscreen) consumed = true;
if (fullscreen) {
consumed = true;
}
fullscreen_prev = fullscreen;
// F4 — Toggle shaders
@@ -63,7 +69,9 @@ namespace GlobalInputs {
Screen::get()->toggleShaders();
Overlay::showNotification(Options::video.shader_enabled ? Locale::get("notifications.shader_on") : Locale::get("notifications.shader_off"));
}
if (shader) consumed = true;
if (shader) {
consumed = true;
}
shader_prev = shader;
// F5 — Toggle aspect ratio 4:3
@@ -72,7 +80,9 @@ namespace GlobalInputs {
Screen::get()->toggleAspectRatio();
Overlay::showNotification(Options::video.aspect_ratio_4_3 ? Locale::get("notifications.aspect_43") : Locale::get("notifications.aspect_square"));
}
if (aspect) consumed = true;
if (aspect) {
consumed = true;
}
aspect_prev = aspect;
// F6 — Toggle supersampling
@@ -82,7 +92,9 @@ namespace GlobalInputs {
Overlay::showNotification(Options::video.supersampling ? Locale::get("notifications.ss_on") : Locale::get("notifications.ss_off"));
}
}
if (ss) consumed = true;
if (ss) {
consumed = true;
}
ss_prev = ss;
// F7 — Canviar tipus de shader (PostFX ↔ CrtPi)
@@ -94,7 +106,9 @@ namespace GlobalInputs {
Overlay::showNotification(msg);
}
}
if (next_shader) consumed = true;
if (next_shader) {
consumed = true;
}
next_shader_prev = next_shader;
// F8 — Pròxim preset del shader actiu
@@ -106,7 +120,9 @@ namespace GlobalInputs {
Overlay::showNotification(msg);
}
}
if (next_preset) consumed = true;
if (next_preset) {
consumed = true;
}
next_preset_prev = next_preset;
// F9 — Cicla filtre de textura (NEAREST ↔ LINEAR), sempre aplicat
@@ -117,7 +133,9 @@ namespace GlobalInputs {
? Locale::get("notifications.filter_linear")
: Locale::get("notifications.filter_nearest"));
}
if (texture_filter) consumed = true;
if (texture_filter) {
consumed = true;
}
texture_filter_prev = texture_filter;
// F10 — Toggle render info (FPS, driver, shader)
@@ -125,7 +143,9 @@ namespace GlobalInputs {
if (render_info && !render_info_prev) {
Overlay::toggleRenderInfo();
}
if (render_info) consumed = true;
if (render_info) {
consumed = true;
}
render_info_prev = render_info;
return consumed;
+33 -11
View File
@@ -16,7 +16,9 @@ namespace KeyConfig {
auto findIndex(const std::string& id) -> size_t {
auto it = index_.find(id);
if (it == index_.end()) return SIZE_MAX;
if (it == index_.end()) {
return SIZE_MAX;
}
return it->second;
}
@@ -30,7 +32,9 @@ namespace KeyConfig {
std::string content(buf.begin(), buf.end());
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("keys")) return;
if (!yaml.contains("keys")) {
return;
}
for (const auto& node : yaml["keys"]) {
KeyEntry entry;
@@ -59,7 +63,9 @@ namespace KeyConfig {
void applyOverrides(const std::string& disk_path) {
std::ifstream file(disk_path);
if (!file.good()) return;
if (!file.good()) {
return;
}
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
@@ -67,7 +73,9 @@ namespace KeyConfig {
try {
auto yaml = fkyaml::node::deserialize(content);
if (!yaml.contains("overrides")) return;
if (!yaml.contains("overrides")) {
return;
}
int applied = 0;
for (const auto& kv : yaml["overrides"].as_map()) {
@@ -118,28 +126,38 @@ namespace KeyConfig {
auto scancode(const std::string& id) -> SDL_Scancode {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return SDL_SCANCODE_UNKNOWN;
if (idx == SIZE_MAX) {
return SDL_SCANCODE_UNKNOWN;
}
return entries_[idx].scancode;
}
auto scancodePtr(const std::string& id) -> SDL_Scancode* {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return nullptr;
if (idx == SIZE_MAX) {
return nullptr;
}
return &entries_[idx].scancode;
}
void setScancode(const std::string& id, SDL_Scancode sc) {
auto idx = findIndex(id);
if (idx == SIZE_MAX) return;
if (idx == SIZE_MAX) {
return;
}
entries_[idx].scancode = sc;
const char* name = SDL_GetScancodeName(sc);
entries_[idx].code = (name != nullptr) ? name : "";
}
auto isGuiKey(SDL_Scancode sc) -> bool {
if (sc == SDL_SCANCODE_UNKNOWN) return false;
if (sc == SDL_SCANCODE_UNKNOWN) {
return false;
}
for (const auto& e : entries_) {
if (e.scancode == sc) return true;
if (e.scancode == sc) {
return true;
}
}
return false;
}
@@ -149,12 +167,16 @@ namespace KeyConfig {
}
auto saveOverrides() -> bool {
if (overrides_path_.empty()) return false;
if (overrides_path_.empty()) {
return false;
}
// Recull només les entrades remapeades.
std::vector<const KeyEntry*> changed;
for (const auto& e : entries_) {
if (e.scancode != e.default_scancode) changed.push_back(&e);
if (e.scancode != e.default_scancode) {
changed.push_back(&e);
}
}
std::ofstream file(overrides_path_);
+3 -1
View File
@@ -17,7 +17,9 @@ namespace KeyRemap {
void update() {
const bool* ks = SDL_GetKeyboardState(nullptr);
if (!ks) return;
if (ks == nullptr) {
return;
}
mirror(Options::keys_game.up, SDL_SCANCODE_UP, ks);
mirror(Options::keys_game.down, SDL_SCANCODE_DOWN, ks);
mirror(Options::keys_game.left, SDL_SCANCODE_LEFT, ks);
+43 -24
View File
@@ -42,18 +42,18 @@ void JD8_ClearScreen(Uint8 color) {
memset(screen, color, 64000);
}
JD8_Surface JD8_NewSurface() {
auto JD8_NewSurface() -> JD8_Surface {
return new Uint8[64000]{};
}
// Helper intern: deriva el basename d'una ruta per a buscar al Cache.
static std::string jd8_basename(const char* file) {
static auto jd8_basename(const char* file) -> std::string {
std::string s = file;
auto pos = s.find_last_of("/\\");
return pos == std::string::npos ? s : s.substr(pos + 1);
}
JD8_Surface JD8_LoadSurface(const char* file) {
auto JD8_LoadSurface(const char* file) -> JD8_Surface {
// Prova primer el Resource::Cache. Si l'asset és precarregat, copiem
// els 64KB des del cache (microsegons) i ens estalviem la decodificació
// GIF. Mantenim el contracte de la funció: el caller rep un buffer
@@ -70,7 +70,8 @@ JD8_Surface JD8_LoadSurface(const char* file) {
}
auto buffer = ResourceHelper::loadFile(file);
unsigned short w, h;
unsigned short w;
unsigned short h;
Uint8* pixels = LoadGif(buffer.data(), &w, &h);
if (pixels == nullptr) {
printf("Unable to load bitmap: %s\n", SDL_GetError());
@@ -82,11 +83,11 @@ JD8_Surface JD8_LoadSurface(const char* file) {
return image;
}
JD8_Palette JD8_LoadPalette(const char* file) {
auto JD8_LoadPalette(const char* file) -> JD8_Palette {
// Sempre retorna un buffer de 256 colors reservat amb `new Color[256]`
// — el caller és responsable d'alliberar-lo amb `delete[]` (o lliurar-ne
// l'ownership a `JD8_SetScreenPalette`).
JD8_Palette palette = new Color[256];
auto palette = new Color[256];
if (Resource::Cache::get() != nullptr) {
try {
@@ -106,7 +107,9 @@ JD8_Palette JD8_LoadPalette(const char* file) {
}
void JD8_SetScreenPalette(JD8_Palette palette) {
if (main_palette == palette) return;
if (main_palette == palette) {
return;
}
delete[] main_palette;
main_palette = palette;
}
@@ -126,9 +129,15 @@ void JD8_FillRect(int x, int y, int w, int h, Uint8 color) {
h += y;
y = 0;
}
if (x + w > 320) w = 320 - x;
if (y + h > 200) h = 200 - y;
if (w <= 0 || h <= 0) return;
if (x + w > 320) {
w = 320 - x;
}
if (y + h > 200) {
h = 200 - y;
}
if (w <= 0 || h <= 0) {
return;
}
for (int row = y; row < y + h; ++row) {
memset(&screen[x + (row * 320)], color, w);
}
@@ -158,47 +167,55 @@ void JD8_BlitToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw
}
}
void JD8_BlitCK(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
void JD8_BlitCK(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
int src_pointer = sx + (sy * 320);
int dst_pointer = x + (y * 320);
for (int j = 0; j < sh; j++) {
for (int i = 0; i < sw; i++) {
if (surface[src_pointer + i] != colorkey) screen[dst_pointer + i] = surface[src_pointer + i];
if (surface[src_pointer + i] != colorkey) {
screen[dst_pointer + i] = surface[src_pointer + i];
}
}
src_pointer += 320;
dst_pointer += 320;
}
}
void JD8_BlitCKCut(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
void JD8_BlitCKCut(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey) {
int src_pointer = sx + (sy * 320);
int dst_pointer = x + (y * 320);
for (int j = 0; j < sh; j++) {
for (int i = 0; i < sw; i++) {
if (surface[src_pointer + i] != colorkey && (x + i >= 0) && (y + j >= 0) && (x + i < 320) && (y + j < 200)) screen[dst_pointer + i] = surface[src_pointer + i];
if (surface[src_pointer + i] != colorkey && (x + i >= 0) && (y + j >= 0) && (x + i < 320) && (y + j < 200)) {
screen[dst_pointer + i] = surface[src_pointer + i];
}
}
src_pointer += 320;
dst_pointer += 320;
}
}
void JD8_BlitCKScroll(int y, JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey) {
void JD8_BlitCKScroll(int y, const JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey) {
int dst_pointer = y * 320;
for (int j = sy; j < sy + sh; j++) {
for (int i = 0; i < 320; i++) {
int x = (i + sx) % 320;
if (surface[x + j * 320] != colorkey) screen[dst_pointer] = surface[x + j * 320];
if (surface[x + (j * 320)] != colorkey) {
screen[dst_pointer] = surface[x + (j * 320)];
}
dst_pointer++;
}
}
}
void JD8_BlitCKToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey) {
void JD8_BlitCKToSurface(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey) {
int src_pointer = sx + (sy * 320);
int dst_pointer = x + (y * 320);
for (int j = 0; j < sh; j++) {
for (int i = 0; i < sw; i++) {
if (surface[src_pointer + i] != colorkey) dest[dst_pointer + i] = surface[src_pointer + i];
if (surface[src_pointer + i] != colorkey) {
dest[dst_pointer + i] = surface[src_pointer + i];
}
}
src_pointer += 320;
dst_pointer += 320;
@@ -218,15 +235,15 @@ void JD8_Flip() {
}
}
Uint32* JD8_GetFramebuffer() {
auto JD8_GetFramebuffer() -> Uint32* {
return pixel_data;
}
void JD8_FreeSurface(JD8_Surface surface) {
void JD8_FreeSurface(const JD8_Surface surface) {
delete[] surface;
}
Uint8 JD8_GetPixel(JD8_Surface surface, int x, int y) {
auto JD8_GetPixel(JD8_Surface surface, int x, int y) -> Uint8 {
return surface[x + (y * 320)];
}
@@ -292,12 +309,14 @@ void JD8_FadeStartToPal(JD8_Palette pal) {
fade_step = 0;
}
bool JD8_FadeIsActive() {
auto JD8_FadeIsActive() -> bool {
return fade_type != FadeType::None;
}
bool JD8_FadeTickStep() {
if (fade_type == FadeType::None) return true;
auto JD8_FadeTickStep() -> bool {
if (fade_type == FadeType::None) {
return true;
}
apply_fade_step();
fade_step++;
+12 -12
View File
@@ -16,11 +16,11 @@ void JD8_Quit();
void JD8_ClearScreen(Uint8 color);
JD8_Surface JD8_NewSurface();
auto JD8_NewSurface() -> JD8_Surface;
JD8_Surface JD8_LoadSurface(const char* file);
auto JD8_LoadSurface(const char* file) -> JD8_Surface;
JD8_Palette JD8_LoadPalette(const char* file);
auto JD8_LoadPalette(const char* file) -> JD8_Palette;
void JD8_SetScreenPalette(JD8_Palette palette);
@@ -36,13 +36,13 @@ void JD8_Blit(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh)
void JD8_BlitToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest);
void JD8_BlitCK(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCK(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCKCut(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCKCut(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, Uint8 colorkey);
void JD8_BlitCKScroll(int y, JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey);
void JD8_BlitCKScroll(int y, const JD8_Surface surface, int sx, int sy, int sh, Uint8 colorkey);
void JD8_BlitCKToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey);
void JD8_BlitCKToSurface(int x, int y, const JD8_Surface surface, int sx, int sy, int sw, int sh, JD8_Surface dest, Uint8 colorkey);
// Converteix la pantalla indexada a ARGB. El Director crida aquesta
// funció al final de cada tick i després llegeix el framebuffer via
@@ -51,11 +51,11 @@ void JD8_Flip();
// Accés al framebuffer ARGB de 320x200 actualitzat per l'última crida a
// JD8_Flip(). Propietat de jdraw8 — el caller no ha de lliberar-lo.
Uint32* JD8_GetFramebuffer();
auto JD8_GetFramebuffer() -> Uint32*;
void JD8_FreeSurface(JD8_Surface surface);
void JD8_FreeSurface(const JD8_Surface surface);
Uint8 JD8_GetPixel(JD8_Surface surface, int x, int y);
auto JD8_GetPixel(JD8_Surface surface, int x, int y) -> Uint8;
void JD8_PutPixel(JD8_Surface surface, int x, int y, Uint8 pixel);
@@ -70,8 +70,8 @@ void JD8_SetPaletteColor(Uint8 index, Uint8 r, Uint8 g, Uint8 b);
// L'embolcall `scenes::PaletteFade` ho fa més idiomàtic per a escenes.
void JD8_FadeStartOut();
void JD8_FadeStartToPal(JD8_Palette pal);
bool JD8_FadeTickStep();
bool JD8_FadeIsActive();
auto JD8_FadeTickStep() -> bool;
auto JD8_FadeIsActive() -> bool;
// JD_Font JD_LoadFont( char *file, int width, int height);
+25 -11
View File
@@ -27,12 +27,16 @@ namespace {
config.clear();
const std::string config_file = config_folder + "/config.txt";
std::ifstream fi(config_file);
if (!fi.is_open()) return;
if (!fi.is_open()) {
return;
}
std::string line;
while (std::getline(fi, line)) {
const auto eq = line.find('=');
if (eq == std::string::npos) continue;
if (eq == std::string::npos) {
continue;
}
config.push_back({line.substr(0, eq), line.substr(eq + 1)});
}
}
@@ -40,7 +44,9 @@ namespace {
void save_config_values() {
const std::string config_file = config_folder + "/config.txt";
std::ofstream fo(config_file);
if (!fo.is_open()) return;
if (!fo.is_open()) {
return;
}
for (const auto& pair : config) {
fo << pair.key << '=' << pair.value << '\n';
}
@@ -52,7 +58,7 @@ void file_setresourcefolder(const char* str) {
resource_folder = str;
}
const char* file_getresourcefolder() {
auto file_getresourcefolder() -> const char* {
return resource_folder.c_str();
}
@@ -76,9 +82,13 @@ void file_setconfigfolder(const char* foldername) {
// arranque dins del navegador. La config no persistirà entre recàrregues
// (MEMFS és volàtil); caldria IDBFS si volguéssem persistència a web.
struct passwd* pw = getpwuid(getuid());
const char* homedir = (pw && pw->pw_dir && pw->pw_dir[0]) ? pw->pw_dir : nullptr;
if (!homedir || !homedir[0]) homedir = getenv("HOME");
if (!homedir || !homedir[0]) homedir = "/tmp";
const char* homedir = ((pw != nullptr) && (pw->pw_dir != nullptr) && (pw->pw_dir[0] != 0)) ? pw->pw_dir : nullptr;
if ((homedir == nullptr) || (homedir[0] == 0)) {
homedir = getenv("HOME");
}
if ((homedir == nullptr) || (homedir[0] == 0)) {
homedir = "/tmp";
}
config_folder = std::string(homedir) + "/.config/" + foldername;
#endif
@@ -92,14 +102,16 @@ void file_setconfigfolder(const char* foldername) {
// volàtil al navegador de totes formes: ignorem l'error i continuem.
}
const char* file_getconfigfolder() {
auto file_getconfigfolder() -> const char* {
thread_local std::string folder;
folder = config_folder + "/";
return folder.c_str();
}
const char* file_getconfigvalue(const char* key) {
if (config.empty()) load_config_values();
auto file_getconfigvalue(const char* key) -> const char* {
if (config.empty()) {
load_config_values();
}
for (const auto& pair : config) {
if (pair.key == key) {
thread_local std::string value_cache;
@@ -111,7 +123,9 @@ const char* file_getconfigvalue(const char* key) {
}
void file_setconfigvalue(const char* key, const char* value) {
if (config.empty()) load_config_values();
if (config.empty()) {
load_config_values();
}
for (auto& pair : config) {
if (pair.key == key) {
pair.value = value;
+3 -3
View File
@@ -1,10 +1,10 @@
#pragma once
void file_setconfigfolder(const char* foldername);
const char* file_getconfigfolder();
auto file_getconfigfolder() -> const char*;
void file_setresourcefolder(const char* str);
const char* file_getresourcefolder();
auto file_getresourcefolder() -> const char*;
const char* file_getconfigvalue(const char* key);
auto file_getconfigvalue(const char* key) -> const char*;
void file_setconfigvalue(const char* key, const char* value);
+4 -4
View File
@@ -24,7 +24,7 @@ void JG_QuitSignal() {
quitting = true;
}
bool JG_Quitting() {
auto JG_Quitting() -> bool {
return quitting;
}
@@ -32,7 +32,7 @@ void JG_SetUpdateTicks(Uint32 milliseconds) {
update_ticks = milliseconds;
}
bool JG_ShouldUpdate() {
auto JG_ShouldUpdate() -> bool {
const Uint32 now = SDL_GetTicks();
if (now - update_time > update_ticks) {
update_time = now;
@@ -45,11 +45,11 @@ bool JG_ShouldUpdate() {
return false;
}
Uint32 JG_GetCycleCounter() {
auto JG_GetCycleCounter() -> Uint32 {
return cycle_counter;
}
Uint32 JG_GetDeltaMs() {
auto JG_GetDeltaMs() -> Uint32 {
const Uint32 now = SDL_GetTicks();
const Uint32 delta = now - last_delta_time;
last_delta_time = now;
+4 -4
View File
@@ -7,14 +7,14 @@ void JG_Finalize();
void JG_QuitSignal();
bool JG_Quitting();
auto JG_Quitting() -> bool;
void JG_SetUpdateTicks(Uint32 milliseconds);
bool JG_ShouldUpdate();
auto JG_ShouldUpdate() -> bool;
Uint32 JG_GetCycleCounter();
auto JG_GetCycleCounter() -> Uint32;
// Temps transcorregut (en ms) des de l'última crida a JG_GetDeltaMs.
// Helper per a la migració progressiva a time-based (Fase 4+).
Uint32 JG_GetDeltaMs();
auto JG_GetDeltaMs() -> Uint32;
+45 -22
View File
@@ -1,5 +1,6 @@
#include "core/jail/jinput.hpp"
#include <algorithm>
#include <cstring>
#include "core/system/director.hpp"
@@ -19,7 +20,7 @@ namespace {
// Temps restant en mil·lisegons durant el qual JI_KeyPressed/JI_AnyKey
// retornen false. Utilitzat per a evitar que pulsacions fortuïtes
// saltin cinemàtiques al començament.
float wait_ms = 0.0f;
float wait_ms = 0.0F;
// Per a calcular el delta entre crides a JI_Update sense que els callers
// hagen de passar-lo explícitament. Es reinicia a la primera crida.
@@ -29,7 +30,7 @@ namespace {
Uint8 virtual_keystates[JI_VSRC_COUNT][SDL_SCANCODE_COUNT] = {{0}};
Uint8 scancode_to_ascii(Uint8 scancode) {
auto scancode_to_ascii(Uint8 scancode) -> Uint8 {
if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
return static_cast<Uint8>('a' + (scancode - SDL_SCANCODE_A));
}
@@ -47,8 +48,12 @@ void JI_SetInputBlocked(bool blocked) {
}
void JI_SetVirtualKey(int scancode, int source, bool pressed) {
if (scancode < 0 || scancode >= SDL_SCANCODE_COUNT) return;
if (source < 0 || source >= JI_VSRC_COUNT) return;
if (scancode < 0 || scancode >= SDL_SCANCODE_COUNT) {
return;
}
if (source < 0 || source >= JI_VSRC_COUNT) {
return;
}
virtual_keystates[source][scancode] = pressed ? 1 : 0;
}
@@ -64,49 +69,67 @@ void JI_Update() {
// El director ha processat tots els events. Ací només refresquem
// el snapshot del teclat i consumim el flag de tecla polsada.
if (keystates == nullptr) {
keystates = SDL_GetKeyboardState(NULL);
keystates = SDL_GetKeyboardState(nullptr);
}
const Uint64 now = SDL_GetTicks();
if (last_update_tick == 0) last_update_tick = now;
const float delta_ms = static_cast<float>(now - last_update_tick);
if (last_update_tick == 0) {
last_update_tick = now;
}
const auto delta_ms = static_cast<float>(now - last_update_tick);
last_update_tick = now;
if (wait_ms > 0.0f) {
if (wait_ms > 0.0F) {
wait_ms -= delta_ms;
if (wait_ms < 0.0f) wait_ms = 0.0f;
wait_ms = std::max(wait_ms, 0.0f);
}
// Consumim el flag de "alguna tecla no-GUI polsada" del director
key_pressed = Director::get()->consumeKeyPressed();
}
bool JI_KeyPressed(int key) {
if (wait_ms > 0.0f || keystates == nullptr) return false;
auto JI_KeyPressed(int key) -> bool {
if (wait_ms > 0.0F || keystates == nullptr) {
return false;
}
// Input bloquejat (p.ex. menú flotant obert)
if (input_blocked) return false;
if (input_blocked) {
return false;
}
// ESC bloquejada pel Director (primera pulsació mostra notificació)
if (key == SDL_SCANCODE_ESCAPE && Director::get()->isEscBlocked()) return false;
if (key < 0 || key >= SDL_SCANCODE_COUNT) return false;
if (keystates[key] != 0) return true;
for (int src = 0; src < JI_VSRC_COUNT; src++) {
if (virtual_keystates[src][key] != 0) return true;
if (key == SDL_SCANCODE_ESCAPE && Director::get()->isEscBlocked()) {
return false;
}
if (key < 0 || key >= SDL_SCANCODE_COUNT) {
return false;
}
if (static_cast<int>(keystates[key]) != 0) {
return true;
}
for (auto& virtual_keystate : virtual_keystates) {
if (virtual_keystate[key] != 0) {
return true;
}
}
return false;
}
bool JI_CheatActivated(const char* cheat_code) {
auto JI_CheatActivated(const char* cheat_code) -> bool {
const size_t len = std::strlen(cheat_code);
if (len > sizeof(cheat)) return false;
if (len > sizeof(cheat)) {
return false;
}
// Compara contra els últims `len` caràcters del buffer. El buffer té
// mida fixa 5 i acumula sempre el darrer tecle a la posició 4.
const size_t offset = sizeof(cheat) - len;
for (size_t i = 0; i < len; i++) {
if (cheat[offset + i] != static_cast<Uint8>(cheat_code[i])) return false;
if (cheat[offset + i] != static_cast<Uint8>(cheat_code[i])) {
return false;
}
}
return true;
}
bool JI_AnyKey() {
return wait_ms > 0.0f ? false : key_pressed;
auto JI_AnyKey() -> bool {
return wait_ms > 0.0F ? false : key_pressed;
}
+4 -4
View File
@@ -12,14 +12,14 @@ void JI_SetInputBlocked(bool blocked);
enum JI_VirtualSource {
JI_VSRC_GAMEPAD = 0,
JI_VSRC_REMAP = 1,
JI_VSRC_COUNT
JI_VSRC_COUNT = 2
};
void JI_SetVirtualKey(int scancode, int source, bool pressed);
void JI_Update();
bool JI_KeyPressed(int key);
auto JI_KeyPressed(int key) -> bool;
bool JI_CheatActivated(const char* cheat_code);
auto JI_CheatActivated(const char* cheat_code) -> bool;
bool JI_AnyKey();
auto JI_AnyKey() -> bool;
+5 -3
View File
@@ -15,7 +15,7 @@ namespace Locale {
static void traverse(const fkyaml::node& node, const std::string& prefix) {
if (node.is_mapping()) {
for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = it.key().get_value<std::string>();
auto key = it.key().get_value<std::string>();
std::string full = prefix.empty() ? key : prefix + "." + key;
traverse(it.value(), full);
}
@@ -26,7 +26,7 @@ namespace Locale {
}
}
bool load(const char* filename) {
auto load(const char* filename) -> bool {
auto buffer = ResourceHelper::loadFile(filename);
if (buffer.empty()) {
std::cerr << "Locale: unable to load " << filename << '\n';
@@ -48,7 +48,9 @@ namespace Locale {
auto get(const char* key) -> const char* {
auto it = strings_.find(key);
if (it != strings_.end()) return it->second.c_str();
if (it != strings_.end()) {
return it->second.c_str();
}
return key; // fallback: retorna la clau mateixa
}
+1 -1
View File
@@ -4,7 +4,7 @@
// Les claus són nested amb notació punt ("menu.items.zoom").
// Si una clau no existeix, Locale::get torna la clau mateixa (útil per debug).
namespace Locale {
bool load(const char* filename);
auto load(const char* filename) -> bool;
// Retorna la cadena associada a la clau. El punter és estable durant tota la
// sessió (no canvia), per tant es pot guardar en const char*.
+114 -72
View File
@@ -1,5 +1,6 @@
#include "core/rendering/menu.hpp"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <functional>
@@ -72,16 +73,20 @@ namespace Menu {
std::string subtitle; // opcional — si no buit, es dibuixa sota el títol
};
static bool isVisible(const Item& it) { return !it.visible || it.visible(); }
static auto isVisible(const Item& it) -> bool { return !it.visible || it.visible(); }
// Troba el pròxim ítem visible en direcció `dir` (±1) a partir de `from`.
// Si cap és visible retorna `from`.
static int nextVisibleCursor(const Page& p, int from, int dir) {
static auto nextVisibleCursor(const Page& p, int from, int dir) -> int {
const int n = static_cast<int>(p.items.size());
if (n <= 0) return from;
if (n <= 0) {
return from;
}
for (int i = 1; i <= n; ++i) {
int idx = ((from + dir * i) % n + n) % n;
if (isVisible(p.items[idx])) return idx;
if (isVisible(p.items[idx])) {
return idx;
}
}
return from;
}
@@ -97,7 +102,7 @@ namespace Menu {
// --- Transició entre pàgines ---
static constexpr float TRANSITION_SPEED = 5.5F; // ~180 ms
static Page transition_outgoing_{"", {}, 0};
static Page transition_outgoing_{.title = "", .items = {}, .cursor = 0};
static bool transition_active_{false};
static float transition_progress_{1.0F};
static int transition_dir_{+1}; // +1 endavant, -1 enrere
@@ -120,19 +125,19 @@ namespace Menu {
// --- Helpers ---
static std::string yesNo(bool b) { return b ? Locale::get("menu.values.yes") : Locale::get("menu.values.no"); }
static std::string onOff(bool b) { return b ? Locale::get("menu.values.on") : Locale::get("menu.values.off"); }
static auto yesNo(bool b) -> std::string { return b ? Locale::get("menu.values.yes") : Locale::get("menu.values.no"); }
static auto onOff(bool b) -> std::string { return b ? Locale::get("menu.values.on") : Locale::get("menu.values.off"); }
// --- Builders de pàgines ---
static Page buildVideo();
static Page buildAudio();
static Page buildControls();
static Page buildGame();
static Page buildSystem();
static auto buildVideo() -> Page;
static auto buildAudio() -> Page;
static auto buildControls() -> Page;
static auto buildGame() -> Page;
static auto buildSystem() -> Page;
static Page buildRoot() {
Page p{Locale::get("menu.titles.root"), {}, 0};
static auto buildRoot() -> Page {
Page p{.title = Locale::get("menu.titles.root"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.video"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildVideo()); }, nullptr});
p.items.push_back({Locale::get("menu.items.audio"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildAudio()); }, nullptr});
p.items.push_back({Locale::get("menu.items.controls"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildControls()); }, nullptr});
@@ -141,8 +146,8 @@ namespace Menu {
return p;
}
static Page buildVideo() {
Page p{Locale::get("menu.titles.video"), {}, 0};
static auto buildVideo() -> Page {
Page p{.title = Locale::get("menu.titles.video"), .items = {}, .cursor = 0};
// Zoom i fullscreen: sense sentit a WASM (el navegador posseix el canvas)
#ifndef __EMSCRIPTEN__
@@ -150,8 +155,9 @@ namespace Menu {
char buf[16];
std::snprintf(buf, sizeof(buf), "%dX", Screen::get()->getZoom());
return std::string(buf); }, [](int dir) {
if (dir < 0) Screen::get()->decZoom();
else if (dir > 0) Screen::get()->incZoom(); }, nullptr, nullptr});
if (dir < 0) { Screen::get()->decZoom();
} else if (dir > 0) { Screen::get()->incZoom();
} }, nullptr, nullptr});
p.items.push_back({Locale::get("menu.items.screen"), ItemKind::Toggle, [] { return std::string(Screen::get()->isFullscreen() ? Locale::get("menu.values.fullscreen") : Locale::get("menu.values.windowed")); }, [](int) { Screen::get()->toggleFullscreen(); }, nullptr, nullptr, nullptr});
#endif
@@ -185,15 +191,18 @@ namespace Menu {
p.items.push_back({Locale::get("menu.items.shader"), ItemKind::Toggle, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }, nullptr, nullptr, nullptr});
p.items.push_back({Locale::get("menu.items.shader_type"), ItemKind::Cycle, [] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevShaderType();
else Screen::get()->nextShaderType(); }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
if (dir < 0) { Screen::get()->prevShaderType();
} else { Screen::get()->nextShaderType();
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
p.items.push_back({Locale::get("menu.items.preset"), ItemKind::Cycle, [] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevPreset();
else Screen::get()->nextPreset(); }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
if (dir < 0) { Screen::get()->prevPreset();
} else { Screen::get()->nextPreset();
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
p.items.push_back({Locale::get("menu.items.supersampling"), ItemKind::Toggle, [] { return onOff(Options::video.supersampling); }, [](int) { Screen::get()->toggleSupersampling(); }, nullptr, nullptr, [] {
if (!Options::video.shader_enabled) return false;
if (!Options::video.shader_enabled) { return false;
}
const char* name = Screen::get()->getActiveShaderName();
return name && std::string(name) == "POSTFX"; }});
#endif
@@ -213,10 +222,10 @@ namespace Menu {
}
// Converteix volum 0..1 a percentatge i ho formata com "50%"
static std::string volPct(float v) {
int pct = static_cast<int>(v * 100.0F + 0.5F);
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
static auto volPct(float v) -> std::string {
int pct = static_cast<int>((v * 100.0F) + 0.5F);
pct = std::max(pct, 0);
pct = std::min(pct, 100);
char buf[8];
std::snprintf(buf, sizeof(buf), "%d%%", pct);
return std::string(buf);
@@ -225,13 +234,13 @@ namespace Menu {
// Canvi +/- d'un volum en steps de 0.05 (5%) amb clamping
static void stepVolume(float& v, int dir) {
v += (dir >= 0 ? 0.05F : -0.05F);
if (v < 0.0F) v = 0.0F;
if (v > 1.0F) v = 1.0F;
v = std::max(v, 0.0F);
v = std::min(v, 1.0F);
Options::applyAudio();
}
static Page buildControls() {
Page p{Locale::get("menu.titles.controls"), {}, 0};
static auto buildControls() -> Page {
Page p{.title = Locale::get("menu.titles.controls"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.move_up"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.up});
p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down});
p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left});
@@ -240,8 +249,8 @@ namespace Menu {
return p;
}
static Page buildAudio() {
Page p{Locale::get("menu.titles.audio"), {}, 0};
static auto buildAudio() -> Page {
Page p{.title = Locale::get("menu.titles.audio"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.master_enable"), ItemKind::Toggle, [] { return onOff(Options::audio.enabled); }, [](int) {
Options::audio.enabled = !Options::audio.enabled;
@@ -264,8 +273,8 @@ namespace Menu {
return p;
}
static Page buildGame() {
Page p{Locale::get("menu.titles.game"), {}, 0};
static auto buildGame() -> Page {
Page p{.title = Locale::get("menu.titles.game"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.use_new_logo"), ItemKind::Toggle, [] { return yesNo(Options::game.use_new_logo); }, [](int) { Options::game.use_new_logo = !Options::game.use_new_logo; }, nullptr});
@@ -276,19 +285,23 @@ namespace Menu {
return p;
}
static Page buildSystem() {
Page p{Locale::get("menu.titles.system"), {}, 0};
static auto buildSystem() -> Page {
Page p{.title = Locale::get("menu.titles.system"), .items = {}, .cursor = 0};
p.subtitle = std::string("v") + Texts::VERSION + " (" + Version::GIT_HASH + ")";
p.items.push_back({Locale::get("menu.items.restart"), ItemKind::Action, nullptr, nullptr, [] {
if (Director::get()) Director::get()->requestRestart();
if (Director::get()) {
Director::get()->requestRestart();
}
},
nullptr,
nullptr});
#ifndef __EMSCRIPTEN__
p.items.push_back({Locale::get("menu.items.exit_game"), ItemKind::Action, nullptr, nullptr, [] {
if (Director::get()) Director::get()->requestQuit();
if (Director::get()) {
Director::get()->requestQuit();
}
},
nullptr,
nullptr});
@@ -307,10 +320,14 @@ namespace Menu {
const Uint8 sb = (src_argb >> 16) & 0xFF;
const Uint8 inv = 255 - sa;
for (int row = y; row < y + h; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = x; col < x + w; col++) {
if (col < 0 || col >= SCREEN_W) continue;
Uint32* p = &buf[col + row * SCREEN_W];
if (col < 0 || col >= SCREEN_W) {
continue;
}
Uint32* p = &buf[col + (row * SCREEN_W)];
Uint32 dst = *p;
Uint8 dr = dst & 0xFF;
Uint8 dg = (dst >> 8) & 0xFF;
@@ -318,17 +335,21 @@ namespace Menu {
Uint8 r = (sr * sa + dr * inv) / 255;
Uint8 g = (sg * sa + dg * inv) / 255;
Uint8 b = (sb * sa + db * inv) / 255;
*p = 0xFF000000u | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
*p = 0xFF000000U | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
}
}
}
static void fillRect(Uint32* buf, int x, int y, int w, int h, Uint32 color) {
for (int row = y; row < y + h; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = x; col < x + w; col++) {
if (col < 0 || col >= SCREEN_W) continue;
buf[col + row * SCREEN_W] = color;
if (col < 0 || col >= SCREEN_W) {
continue;
}
buf[col + (row * SCREEN_W)] = color;
}
}
}
@@ -343,12 +364,14 @@ namespace Menu {
// Mida final de la caixa segons el nombre d'items *visibles*.
// body = (N-1) * ITEM_SPACING + charH — així BOTTOM_PAD és el buit real
// sota el text del darrer ítem, no un buit extra per sobre d'un "slot" buit.
static int boxHeight(const Page& page) {
static auto boxHeight(const Page& page) -> int {
int n = 0;
for (const auto& it : page.items) {
if (isVisible(it)) ++n;
if (isVisible(it)) {
++n;
}
}
int body = (n == 0) ? 8 : (n - 1) * ITEM_SPACING + 8;
int body = (n == 0) ? 8 : ((n - 1) * ITEM_SPACING) + 8;
int header = HEADER_H + (page.subtitle.empty() ? 0 : SUBTITLE_H);
return header + body + BOTTOM_PAD;
}
@@ -403,7 +426,9 @@ namespace Menu {
// render() faça decréixer open_anim_ fins a 0. En aquell moment es neteja
// l'estat. Si es crida estant ja tancat o tancant-se, no-op.
void close() {
if (stack_.empty() || closing_) return;
if (stack_.empty() || closing_) {
return;
}
closing_ = true;
capturing_ = nullptr;
transition_active_ = false;
@@ -416,7 +441,9 @@ namespace Menu {
}
void captureKey(SDL_Scancode sc) {
if (!capturing_) return;
if (capturing_ == nullptr) {
return;
}
if (sc == SDL_SCANCODE_ESCAPE) {
// Cancel·la
capturing_ = nullptr;
@@ -427,15 +454,18 @@ namespace Menu {
}
void handleKey(SDL_Scancode sc) {
if (!isOpen()) return;
if (!isOpen()) {
return;
}
Page& page = stack_.back();
if (page.items.empty()) {
// Pàgina buida — només backspace surt
if (sc == SDL_SCANCODE_BACKSPACE) {
if (stack_.size() > 1)
if (stack_.size() > 1) {
popPage();
else
} else {
close();
}
}
return;
}
@@ -467,7 +497,9 @@ namespace Menu {
case SDL_SCANCODE_KP_ENTER:
if (page.items[page.cursor].kind == ItemKind::Submenu ||
page.items[page.cursor].kind == ItemKind::Action) {
if (page.items[page.cursor].enter) page.items[page.cursor].enter();
if (page.items[page.cursor].enter) {
page.items[page.cursor].enter();
}
} else if (page.items[page.cursor].kind == ItemKind::KeyBind) {
capturing_ = page.items[page.cursor].scancode;
} else if (page.items[page.cursor].change) {
@@ -475,10 +507,11 @@ namespace Menu {
}
break;
case SDL_SCANCODE_BACKSPACE:
if (stack_.size() > 1)
if (stack_.size() > 1) {
popPage();
else
} else {
close();
}
break;
default:
break;
@@ -500,7 +533,7 @@ namespace Menu {
static void renderPageContent(Uint32* pixel_data, const Page& page, int box_x, int box_y, int x_offset, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) {
// Títol
int title_w = font_->width(page.title);
int title_x = box_x + (BOX_W - title_w) / 2 + x_offset;
int title_x = box_x + ((BOX_W - title_w) / 2) + x_offset;
font_->drawClipped(pixel_data, title_x, box_y + TITLE_PAD_Y, page.title, TITLE_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
// Línia sota el títol (també lliscada) — clippada manualment
@@ -519,26 +552,31 @@ namespace Menu {
int items_y = title_line_y + 4;
if (!page.subtitle.empty()) {
int sub_w = font_->width(page.subtitle.c_str());
int sub_x = box_x + (BOX_W - sub_w) / 2 + x_offset;
int sub_x = box_x + ((BOX_W - sub_w) / 2) + x_offset;
font_->drawClipped(pixel_data, sub_x, items_y, page.subtitle.c_str(), LABEL_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
items_y += SUBTITLE_H;
}
// Compta visibles — si cap, dibuixa placeholder (caixa totalment col·lapsada però oberta)
int visible_count = 0;
for (const auto& it : page.items)
if (isVisible(it)) ++visible_count;
for (const auto& it : page.items) {
if (isVisible(it)) {
++visible_count;
}
}
if (visible_count == 0) {
const char* empty_text = Locale::get("menu.values.empty");
int ew = font_->width(empty_text);
font_->drawClipped(pixel_data, box_x + (BOX_W - ew) / 2 + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
font_->drawClipped(pixel_data, box_x + ((BOX_W - ew) / 2) + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
return;
}
int y_slot = 0; // índex de fila visible (independent de l'índex real de l'ítem)
for (size_t i = 0; i < page.items.size(); i++) {
const Item& item = page.items[i];
if (!isVisible(item)) continue;
int y = items_y + y_slot * ITEM_SPACING;
if (!isVisible(item)) {
continue;
}
int y = items_y + (y_slot * ITEM_SPACING);
++y_slot;
bool selected = (static_cast<int>(i) == page.cursor);
Uint32 label_color = selected ? CURSOR_COLOR : LABEL_COLOR;
@@ -546,7 +584,7 @@ namespace Menu {
// Action: sense valor a la dreta — centrem el label amb el cursor just a l'esquerra.
if (item.kind == ItemKind::Action) {
int lw = font_->width(item.label);
int lx = box_x + (BOX_W - lw) / 2 + x_offset;
int lx = box_x + ((BOX_W - lw) / 2) + x_offset;
if (selected) {
font_->drawClipped(pixel_data, lx - font_->width("> "), y, ">", CURSOR_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
}
@@ -567,8 +605,10 @@ namespace Menu {
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - aw + x_offset, y, arrow, ac, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
} else if (item.kind == ItemKind::KeyBind) {
bool this_capturing = (capturing_ == item.scancode);
const char* text = this_capturing ? Locale::get("menu.values.press_key") : (item.scancode ? SDL_GetScancodeName(*item.scancode) : "");
if (!text || !*text) text = Locale::get("menu.values.unknown");
const char* text = this_capturing ? Locale::get("menu.values.press_key") : ((item.scancode != nullptr) ? SDL_GetScancodeName(*item.scancode) : "");
if ((text == nullptr) || (*text == 0)) {
text = Locale::get("menu.values.unknown");
}
int tw = font_->width(text);
Uint32 tc = this_capturing ? 0xFF00FFFF : (selected ? CURSOR_COLOR : VALUE_COLOR);
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - tw + x_offset, y, text, tc, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
@@ -582,7 +622,9 @@ namespace Menu {
}
void render(Uint32* pixel_data) {
if (!isVisible() || !font_ || !pixel_data) return;
if (!isVisible() || !font_ || (pixel_data == nullptr)) {
return;
}
// Delta time
Uint32 now = SDL_GetTicks();
@@ -600,7 +642,7 @@ namespace Menu {
}
} else if (open_anim_ < 1.0F) {
open_anim_ += OPEN_SPEED * dt;
if (open_anim_ > 1.0F) open_anim_ = 1.0F;
open_anim_ = std::min(open_anim_, 1.0F);
}
// Avança transició
@@ -626,7 +668,7 @@ namespace Menu {
animated_h_ = static_cast<float>(current_h);
} else {
float t = HEIGHT_RATE * dt;
if (t > 1.0F) t = 1.0F;
t = std::min(t, 1.0F);
animated_h_ += diff * t;
}
}
@@ -643,12 +685,12 @@ namespace Menu {
// Caixa creix verticalment durant l'obertura
int box_h = static_cast<int>(target_h * eased);
if (box_h < 2) box_h = 2;
box_h = std::max(box_h, 2);
int box_x = (SCREEN_W - BOX_W) / 2;
int box_y = (SCREEN_H - box_h) / 2;
// Fons semi-transparent (alpha escalat per l'animació d'obertura)
Uint8 alpha = static_cast<Uint8>(BG_ALPHA * eased);
auto alpha = static_cast<Uint8>(BG_ALPHA * eased);
blendRect(pixel_data, box_x, box_y, BOX_W, box_h, BG_COLOR, alpha);
// El contingut només apareix quan la caixa és prou gran
+25 -19
View File
@@ -102,16 +102,22 @@ namespace Overlay {
// Pinta un rectangle sòlid dins els límits de la pantalla
static void drawRect(Uint32* pixel_data, int rx, int ry, int rw, int rh, Uint32 color) {
for (int row = ry; row < ry + rh; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = rx; col < rx + rw; col++) {
if (col < 0 || col >= SCREEN_W) continue;
pixel_data[col + row * SCREEN_W] = color;
if (col < 0 || col >= SCREEN_W) {
continue;
}
pixel_data[col + (row * SCREEN_W)] = color;
}
}
}
void render(Uint32* pixel_data) {
if (!font_ || !pixel_data) return;
if (!font_ || (pixel_data == nullptr)) {
return;
}
// Calcula delta time
Uint32 now = SDL_GetTicks();
@@ -148,7 +154,9 @@ namespace Overlay {
break;
}
if (notif.status == Status::FINISHED) continue;
if (notif.status == Status::FINISHED) {
continue;
}
// Posició segons el tipus
int box_x = 0;
@@ -180,7 +188,7 @@ namespace Overlay {
int line_y = box_y + NOTIF_PADDING_V;
for (const auto& line : notif.lines) {
int line_w = font_->width(line.c_str());
int line_x = box_x + (notif.box_w - line_w) / 2; // centrat dins la caixa
int line_x = box_x + ((notif.box_w - line_w) / 2); // centrat dins la caixa
if (notif.style == NotifStyle::SHADOW) {
font_->draw(pixel_data, line_x + 1, line_y + 1, line.c_str(), notif.accent_color);
} else if (notif.style == NotifStyle::OUTLINE) {
@@ -203,7 +211,7 @@ namespace Overlay {
// Mateix lloc: entra fins a 1
if (info_anim_ < 1.0F) {
info_anim_ += INFO_SLIDE_SPEED * dt;
if (info_anim_ > 1.0F) info_anim_ = 1.0F;
info_anim_ = std::min(info_anim_, 1.0F);
}
} else {
// Canvi: si visible_pos està OFF, commuta directament
@@ -225,10 +233,10 @@ namespace Overlay {
float target = seg.visible ? 1.0F : 0.0F;
if (seg.anim < target) {
seg.anim += SEG_SPEED * dt;
if (seg.anim > target) seg.anim = target;
seg.anim = std::min(seg.anim, target);
} else if (seg.anim > target) {
seg.anim -= SEG_SPEED * dt;
if (seg.anim < target) seg.anim = target;
seg.anim = std::max(seg.anim, target);
}
}
@@ -283,9 +291,7 @@ namespace Overlay {
}
// Elimina les acabades
notifications_.erase(
std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& n) { return n.status == Status::FINISHED; }),
notifications_.end());
std::erase_if(notifications_, [](const Notification& n) { return n.status == Status::FINISHED; });
// Si la notificació d'ESC ha desaparegut, reseteja l'estat
if (esc_waiting_ && notifications_.empty()) {
@@ -293,7 +299,7 @@ namespace Overlay {
}
// Indicador de pausa persistent (cantó superior dret)
if (Director::get() && Director::get()->isPaused()) {
if ((Director::get() != nullptr) && Director::get()->isPaused()) {
const char* pause_text = Locale::get("notifications.pause");
int w = font_->width(pause_text);
int x = SCREEN_W - w - 4;
@@ -357,9 +363,7 @@ namespace Overlay {
}
// Neteja notificacions finalitzades
notifications_.erase(
std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& n) { return n.status == Status::FINISHED; }),
notifications_.end());
std::erase_if(notifications_, [](const Notification& n) { return n.status == Status::FINISHED; });
// Menú flotant per damunt de tot (isVisible inclou l'animació de tancament)
if (Menu::isVisible()) {
@@ -392,7 +396,7 @@ namespace Overlay {
int max_w = 0;
for (const auto& line : lines) {
int w = font_->width(line.c_str());
if (w > max_w) max_w = w;
max_w = std::max(w, max_w);
}
notif.box_w = max_w + NOTIF_PADDING_H * 2;
int line_h = font_->charHeight();
@@ -414,7 +418,7 @@ namespace Overlay {
void setRenderInfoSegments(const char* s0, const char* s1, const char* s2, const char* s3, unsigned int mono_mask) {
const char* segs[INFO_SEGMENT_COUNT] = {s0, s1, s2, s3};
for (int i = 0; i < INFO_SEGMENT_COUNT; i++) {
info_segments_[i].mono_digits = (mono_mask >> i) & 1u;
info_segments_[i].mono_digits = (((mono_mask >> i) & 1U) != 0u);
if (segs[i] != nullptr && *segs[i] != '\0') {
info_segments_[i].text = segs[i];
info_segments_[i].visible = true;
@@ -425,7 +429,9 @@ namespace Overlay {
}
void startCredits() {
if (credits_phase_ != CreditsPhase::IDLE) return;
if (credits_phase_ != CreditsPhase::IDLE) {
return;
}
credits_phase_ = CreditsPhase::DELAY;
credits_timer_ = 0.0F;
}
+104 -41
View File
@@ -1,5 +1,6 @@
#include "core/rendering/screen.hpp"
#include <algorithm>
#include <cstdio>
#include <iostream>
@@ -76,15 +77,15 @@ Screen::Screen() {
calculateMaxZoom();
if (zoom_ < 1) zoom_ = 1;
if (zoom_ > max_zoom_) zoom_ = max_zoom_;
zoom_ = std::max(zoom_, 1);
zoom_ = std::min(zoom_, max_zoom_);
// Clamp de la resolució interna a [1, max_zoom_]. Llegir del YAML i
// ajustar aquí és l'únic moment en què es fa — el menú re-clampa cada
// canvi. Si la pantalla és més petita que el valor desat (p.ex. canvi
// de monitor), baixem al màxim suportat.
if (Options::video.internal_resolution < 1) Options::video.internal_resolution = 1;
if (Options::video.internal_resolution > max_zoom_) Options::video.internal_resolution = max_zoom_;
Options::video.internal_resolution = std::max(Options::video.internal_resolution, 1);
Options::video.internal_resolution = std::min(Options::video.internal_resolution, max_zoom_);
int w = GAME_WIDTH * zoom_;
int h = Options::video.aspect_ratio_4_3 ? static_cast<int>(GAME_HEIGHT * 1.2F) * zoom_ : GAME_HEIGHT * zoom_;
@@ -124,15 +125,25 @@ Screen::~Screen() {
if (shader_backend_) {
#ifndef NO_SHADERS
auto* gpu = dynamic_cast<Rendering::SDL3GPUShader*>(shader_backend_.get());
if (gpu) gpu->destroy();
if (gpu != nullptr) {
gpu->destroy();
}
#endif
shader_backend_.reset();
}
if (internal_texture_sdl_) SDL_DestroyTexture(internal_texture_sdl_);
if (texture_) SDL_DestroyTexture(texture_);
if (renderer_) SDL_DestroyRenderer(renderer_);
if (window_) SDL_DestroyWindow(window_);
if (internal_texture_sdl_ != nullptr) {
SDL_DestroyTexture(internal_texture_sdl_);
}
if (texture_ != nullptr) {
SDL_DestroyTexture(texture_);
}
if (renderer_ != nullptr) {
SDL_DestroyRenderer(renderer_);
}
if (window_ != nullptr) {
SDL_DestroyWindow(window_);
}
}
void Screen::initShaders() {
@@ -143,7 +154,9 @@ void Screen::initShaders() {
// curtcircuiten cap al fallback SDL_Renderer.
return;
#else
if (!Options::video.gpu_acceleration) return;
if (!Options::video.gpu_acceleration) {
return;
}
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
@@ -282,19 +295,25 @@ void Screen::toggleFullscreen() {
}
void Screen::incZoom() {
if (fullscreen_ || zoom_ >= max_zoom_) return;
if (fullscreen_ || zoom_ >= max_zoom_) {
return;
}
zoom_++;
adjustWindowSize();
}
void Screen::decZoom() {
if (fullscreen_ || zoom_ <= 1) return;
if (fullscreen_ || zoom_ <= 1) {
return;
}
zoom_--;
adjustWindowSize();
}
void Screen::setZoom(int zoom) {
if (zoom < 1 || zoom > max_zoom_ || fullscreen_) return;
if (zoom < 1 || zoom > max_zoom_ || fullscreen_) {
return;
}
zoom_ = zoom;
adjustWindowSize();
}
@@ -310,9 +329,15 @@ auto Screen::toggleSupersampling() -> bool {
// SS només té sentit amb shaders on i pipeline PostFX (el Lanczos downscale
// i el camí SS s'apliquen al pas de PostFX; CRTPI fa el seu propi
// submostreig intern i no usa aquesta via).
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (shader_backend_->getActiveShader() != Rendering::ShaderType::POSTFX) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() != Rendering::ShaderType::POSTFX) {
return false;
}
Options::video.supersampling = !Options::video.supersampling;
shader_backend_->setOversample(Options::video.supersampling ? 3 : 1);
return true;
@@ -366,9 +391,11 @@ void Screen::cycleTextureFilter(int dir) {
void Screen::changeInternalResolution(int dir) {
int next = Options::video.internal_resolution + (dir >= 0 ? 1 : -1);
if (next < 1) next = 1;
if (next > max_zoom_) next = max_zoom_;
if (next == Options::video.internal_resolution) return;
next = std::max(next, 1);
next = std::min(next, max_zoom_);
if (next == Options::video.internal_resolution) {
return;
}
Options::video.internal_resolution = next;
// Propaga al backend actiu. Al fallback path, la textura es recrea al
@@ -381,8 +408,12 @@ void Screen::changeInternalResolution(int dir) {
}
auto Screen::nextShaderType() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI);
@@ -397,16 +428,24 @@ auto Screen::nextShaderType() -> bool {
}
auto Screen::nextPreset() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::postfx_presets.empty()) return false;
if (Options::postfx_presets.empty()) {
return false;
}
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
Options::video.current_postfx_preset = Options::postfx_presets[Options::current_postfx_preset].name;
applyCurrentPostFXPreset();
} else {
if (Options::crtpi_presets.empty()) return false;
if (Options::crtpi_presets.empty()) {
return false;
}
Options::current_crtpi_preset = (Options::current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
Options::video.current_crtpi_preset = Options::crtpi_presets[Options::current_crtpi_preset].name;
applyCurrentCrtPiPreset();
@@ -420,17 +459,25 @@ auto Screen::prevShaderType() -> bool {
}
auto Screen::prevPreset() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::postfx_presets.empty()) return false;
if (Options::postfx_presets.empty()) {
return false;
}
int n = static_cast<int>(Options::postfx_presets.size());
Options::current_postfx_preset = (Options::current_postfx_preset - 1 + n) % n;
Options::video.current_postfx_preset = Options::postfx_presets[Options::current_postfx_preset].name;
applyCurrentPostFXPreset();
} else {
if (Options::crtpi_presets.empty()) return false;
if (Options::crtpi_presets.empty()) {
return false;
}
int n = static_cast<int>(Options::crtpi_presets.size());
Options::current_crtpi_preset = (Options::current_crtpi_preset - 1 + n) % n;
Options::video.current_crtpi_preset = Options::crtpi_presets[Options::current_crtpi_preset].name;
@@ -440,13 +487,17 @@ auto Screen::prevPreset() -> bool {
}
auto Screen::getCurrentPresetName() const -> const char* {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return "---";
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return "---";
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::current_postfx_preset < static_cast<int>(Options::postfx_presets.size()))
if (Options::current_postfx_preset < static_cast<int>(Options::postfx_presets.size())) {
return Options::postfx_presets[Options::current_postfx_preset].name.c_str();
}
} else {
if (Options::current_crtpi_preset < static_cast<int>(Options::crtpi_presets.size()))
if (Options::current_crtpi_preset < static_cast<int>(Options::crtpi_presets.size())) {
return Options::crtpi_presets[Options::current_crtpi_preset].name.c_str();
}
}
return "---";
}
@@ -458,7 +509,9 @@ void Screen::setActiveShader(Rendering::ShaderType type) {
}
void Screen::applyCurrentPostFXPreset() {
if (!shader_backend_ || Options::postfx_presets.empty()) return;
if (!shader_backend_ || Options::postfx_presets.empty()) {
return;
}
const auto& preset = Options::postfx_presets[Options::current_postfx_preset];
Rendering::PostFXParams p;
p.vignette = preset.vignette;
@@ -473,7 +526,9 @@ void Screen::applyCurrentPostFXPreset() {
}
void Screen::applyCurrentCrtPiPreset() {
if (!shader_backend_ || Options::crtpi_presets.empty()) return;
if (!shader_backend_ || Options::crtpi_presets.empty()) {
return;
}
const auto& preset = Options::crtpi_presets[Options::current_crtpi_preset];
Rendering::CrtPiParams p;
p.scanline_weight = preset.scanline_weight;
@@ -498,7 +553,9 @@ auto Screen::isHardwareAccelerated() const -> bool {
}
auto Screen::getActiveShaderName() const -> const char* {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return "SENSE GPU";
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return "SENSE GPU";
}
return shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX ? "POSTFX" : "CRT-PI";
}
@@ -534,7 +591,7 @@ void Screen::updateRenderInfo() {
fps_driver.c_str(),
shader_seg.empty() ? nullptr : shader_seg.c_str(),
ss_seg,
time_buf[0] ? time_buf : nullptr,
(time_buf[0] != 0) ? time_buf : nullptr,
0b1001);
}
@@ -544,7 +601,9 @@ void Screen::applyFallbackPresentation() {
SDL_ScaleMode scale = (Options::video.texture_filter == Options::TextureFilter::LINEAR)
? SDL_SCALEMODE_LINEAR
: SDL_SCALEMODE_NEAREST;
if (texture_) SDL_SetTextureScaleMode(texture_, scale);
if (texture_ != nullptr) {
SDL_SetTextureScaleMode(texture_, scale);
}
// Si 4:3 actiu, la finestra ja té aspect 4:3 (alçada × 1.2); STRETCH és
// l'única opció viable al path fallback (el GPU path fa l'upscale 4:3 abans
@@ -578,7 +637,9 @@ void Screen::applyFallbackPresentation() {
}
void Screen::ensureFallbackInternalTexture() {
if (renderer_ == nullptr) return;
if (renderer_ == nullptr) {
return;
}
const int mult = Options::video.internal_resolution;
if (mult <= 1) {
// No cal textura intermèdia — recicla si la teníem.
@@ -589,7 +650,9 @@ void Screen::ensureFallbackInternalTexture() {
}
return;
}
if (internal_texture_sdl_ != nullptr && internal_texture_mult_ == mult) return;
if (internal_texture_sdl_ != nullptr && internal_texture_mult_ == mult) {
return;
}
if (internal_texture_sdl_ != nullptr) {
SDL_DestroyTexture(internal_texture_sdl_);
@@ -620,11 +683,11 @@ void Screen::adjustWindowSize() {
void Screen::calculateMaxZoom() {
SDL_DisplayID display = SDL_GetPrimaryDisplay();
const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display);
if (mode) {
if (mode != nullptr) {
int max_w = mode->w / GAME_WIDTH;
int max_h = mode->h / GAME_HEIGHT;
max_zoom_ = (max_w < max_h) ? max_w : max_h;
if (max_zoom_ < 1) max_zoom_ = 1;
max_zoom_ = std::max(max_zoom_, 1);
}
}
@@ -906,8 +906,8 @@ namespace Rendering {
// ---- Calcular viewport (dimensions lògiques del canvas) ----
// Si 4:3 actiu, effective_height ja és 240 (la textura estirada)
const float logical_w = static_cast<float>(game_width_);
const float logical_h = static_cast<float>(effective_height);
const auto logical_w = static_cast<float>(game_width_);
const auto logical_h = static_cast<float>(effective_height);
float vx = 0.0F;
float vy = 0.0F;
@@ -1276,7 +1276,9 @@ namespace Rendering {
// Recrea la textura intermèdia amb les noves dimensions (320·N × 200·N).
void SDL3GPUShader::setInternalResolution(int multiplier) {
const int NEW = std::max(1, multiplier);
if (NEW == internal_res_) return;
if (NEW == internal_res_) {
return;
}
internal_res_ = NEW;
if (is_initialized_ && device_ != nullptr) {
SDL_WaitForGPUIdle(device_);
@@ -1286,7 +1288,9 @@ namespace Rendering {
void SDL3GPUShader::setStretch4_3(bool enabled) {
stretch_4_3_ = enabled;
if (!is_initialized_ || device_ == nullptr) return;
if (!is_initialized_ || device_ == nullptr) {
return;
}
// Recrear scaled_texture_ perquè tinga les dimensions correctes (amb o sense 4:3)
if (oversample_ > 1 && ss_factor_ > 0) {
@@ -1464,7 +1468,9 @@ namespace Rendering {
SDL_ReleaseGPUTexture(device_, internal_texture_);
internal_texture_ = nullptr;
}
if (internal_res_ <= 1 || device_ == nullptr) return true;
if (internal_res_ <= 1 || device_ == nullptr) {
return true;
}
const int W = game_width_ * internal_res_;
const int H = game_height_ * internal_res_;
+127 -57
View File
@@ -12,7 +12,7 @@
// Forward declarations de gif.h (inclòs des de jdraw8.cpp, no es pot incloure dos vegades)
struct rgb;
extern unsigned char* LoadGif(unsigned char* data, unsigned short* w, unsigned short* h);
extern auto LoadGif(unsigned char* data, unsigned short* w, unsigned short* h) -> unsigned char*;
Text::Text(const char* fnt_file, const char* gif_file) {
loadBitmap(gif_file);
@@ -23,7 +23,9 @@ Text::Text(const char* fnt_file, const char* gif_file) {
auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
auto byte = static_cast<uint8_t>(*ptr);
if (byte == 0) return 0;
if (byte == 0) {
return 0;
}
uint32_t cp = 0;
int extra = 0;
@@ -47,7 +49,9 @@ auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
ptr++;
for (int i = 0; i < extra; i++) {
auto cont = static_cast<uint8_t>(*ptr);
if ((cont & 0xC0) != 0x80) return 0xFFFD;
if ((cont & 0xC0) != 0x80) {
return 0xFFFD;
}
cp = (cp << 6) | (cont & 0x3F);
ptr++;
}
@@ -71,7 +75,9 @@ void Text::loadFont(const char* fnt_file) {
while (std::getline(stream, line)) {
// Ignora comentaris i línies buides
if (line.empty() || line[0] == '#') continue;
if (line.empty() || line[0] == '#') {
continue;
}
// Elimina comentaris inline
auto comment_pos = line.find('#');
@@ -133,9 +139,10 @@ void Text::loadBitmap(const char* gif_file) {
int w = raw[6] | (raw[7] << 8);
int h = raw[8] | (raw[9] << 8);
unsigned short gw = 0, gh = 0;
unsigned short gw = 0;
unsigned short gh = 0;
Uint8* pixels = LoadGif(raw, &gw, &gh);
if (!pixels) {
if (pixels == nullptr) {
std::cerr << "Text: unable to decode GIF: " << gif_file << '\n';
return;
}
@@ -151,14 +158,18 @@ void Text::loadBitmap(const char* gif_file) {
// --- Renderitzat ---
void Text::draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -174,21 +185,27 @@ void Text::draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color
// Pinta glifo pixel a pixel
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = cursor_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
// Píxel no transparent (índex 0 és fons típicament)
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -204,17 +221,23 @@ void Text::drawCentered(Uint32* pixel_data, int y, const char* text, Uint32 colo
}
void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
// Descart ràpid si el glifo sencer cau fora verticalment
if (y + box_height_ <= clip_y_min || y >= clip_y_max) return;
if (y + box_height_ <= clip_y_min || y >= clip_y_max) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -235,21 +258,31 @@ void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint3
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < clip_y_min || dst_y >= clip_y_max) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
if (dst_y < clip_y_min || dst_y >= clip_y_max) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = cursor_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < clip_x_min || dst_x >= clip_x_max) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
if (dst_x < clip_x_min || dst_x >= clip_x_max) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -259,14 +292,18 @@ void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint3
}
void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int cell_w) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -279,23 +316,29 @@ void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 c
const auto& glyph = it->second;
// Centra el glif dins la cel·la
int glyph_x = cursor_x + (cell_w - glyph.w) / 2;
int glyph_x = cursor_x + ((cell_w - glyph.w) / 2);
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = glyph_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -305,21 +348,27 @@ void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 c
}
void Text::drawMonoDigits(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int digit_cell_w) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
it = glyphs_.find('?');
if (it == glyphs_.end()) {
if (!first) cursor_x += 1;
if (!first) {
cursor_x += 1;
}
cursor_x += box_width_;
first = false;
continue;
@@ -329,22 +378,30 @@ void Text::drawMonoDigits(Uint32* pixel_data, int x, int y, const char* text, Ui
const auto& glyph = it->second;
bool is_digit = (cp >= '0' && cp <= '9');
if (!first) cursor_x += 1; // kerning
if (!first) {
cursor_x += 1; // kerning
}
int glyph_x = is_digit ? cursor_x + (digit_cell_w - glyph.w) / 2 : cursor_x;
int glyph_x = is_digit ? cursor_x + ((digit_cell_w - glyph.w) / 2) : cursor_x;
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = glyph_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -358,32 +415,41 @@ auto Text::widthMonoDigits(const char* text, int digit_cell_w) const -> int {
const char* ptr = text;
int w = 0;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (!first) w += 1; // kerning
if (cp == 0) {
break;
}
if (!first) {
w += 1; // kerning
}
first = false;
bool is_digit = (cp >= '0' && cp <= '9');
if (is_digit) {
w += digit_cell_w;
} else {
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) it = glyphs_.find('?');
if (it != glyphs_.end())
if (it == glyphs_.end()) {
it = glyphs_.find('?');
}
if (it != glyphs_.end()) {
w += it->second.w;
else
} else {
w += box_width_;
}
}
}
return w;
}
auto Text::widthMono(const char* text, int cell_w) const -> int {
auto Text::widthMono(const char* text, int cell_w) -> int {
const char* ptr = text;
int count = 0;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
count++;
}
return count * cell_w;
@@ -394,16 +460,20 @@ auto Text::width(const char* text) const -> int {
int w = 0;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
it = glyphs_.find('?');
}
if (!first) w += 1; // kerning
if (!first) {
w += 1; // kerning
}
first = false;
if (it != glyphs_.end()) {
+1 -1
View File
@@ -28,7 +28,7 @@ class Text {
// Calcula ancho en píxeles d'un text
[[nodiscard]] auto width(const char* text) const -> int;
// Amplada mono: nombre de codepoints × cell_w
[[nodiscard]] auto widthMono(const char* text, int cell_w) const -> int;
[[nodiscard]] static auto widthMono(const char* text, int cell_w) -> int;
// Amplada mono-dígits: amplada natural, però substituint els dígits per digit_cell_w
[[nodiscard]] auto widthMonoDigits(const char* text, int digit_cell_w) const -> int;
[[nodiscard]] auto charHeight() const -> int { return box_height_; }
+23 -9
View File
@@ -15,8 +15,8 @@
// ni inline, així que no podem tornar-lo a incloure aquí. Ens fiem de les
// declaracions extern dels símbols que ens calen (linkatge C++ normal,
// igual que fa text.cpp).
extern unsigned char* LoadGif(unsigned char* data, unsigned short* w, unsigned short* h);
extern unsigned char* LoadPalette(unsigned char* data);
extern auto LoadGif(unsigned char* data, unsigned short* w, unsigned short* h) -> unsigned char*;
extern auto LoadPalette(unsigned char* data) -> unsigned char*;
namespace Resource {
@@ -90,7 +90,9 @@ namespace Resource {
}
auto Cache::getProgress() const -> float {
if (total_count_ == 0) return 1.0F;
if (total_count_ == 0) {
return 1.0F;
}
return static_cast<float>(loaded_count_) / static_cast<float>(total_count_);
}
@@ -102,7 +104,9 @@ namespace Resource {
}
auto Cache::loadStep(int budget_ms) -> bool {
if (stage_ == LoadStage::DONE) return true;
if (stage_ == LoadStage::DONE) {
return true;
}
const Uint64 start_ns = SDL_GetTicksNS();
const Uint64 budget_ns = static_cast<Uint64>(budget_ms) * 1'000'000ULL;
@@ -112,7 +116,9 @@ namespace Resource {
switch (stage_) {
case LoadStage::MUSICS: {
auto items = list->getListByType(List::Type::MUSIC);
if (stage_index_ == 0) musics_.clear();
if (stage_index_ == 0) {
musics_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::SOUNDS;
stage_index_ = 0;
@@ -123,7 +129,9 @@ namespace Resource {
}
case LoadStage::SOUNDS: {
auto items = list->getListByType(List::Type::SOUND);
if (stage_index_ == 0) sounds_.clear();
if (stage_index_ == 0) {
sounds_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::BITMAPS;
stage_index_ = 0;
@@ -134,7 +142,9 @@ namespace Resource {
}
case LoadStage::BITMAPS: {
auto items = list->getListByType(List::Type::BITMAP);
if (stage_index_ == 0) surfaces_.clear();
if (stage_index_ == 0) {
surfaces_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::TEXT_FILES;
stage_index_ = 0;
@@ -148,7 +158,9 @@ namespace Resource {
auto font_items = list->getListByType(List::Type::FONT);
auto items = data_items;
items.insert(items.end(), font_items.begin(), font_items.end());
if (stage_index_ == 0) text_files_.clear();
if (stage_index_ == 0) {
text_files_.clear();
}
if (stage_index_ >= items.size()) {
stage_ = LoadStage::DONE;
stage_index_ = 0;
@@ -161,7 +173,9 @@ namespace Resource {
case LoadStage::DONE:
break;
}
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) break;
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) {
break;
}
}
return stage_ == LoadStage::DONE;
+6 -2
View File
@@ -16,13 +16,17 @@ namespace ResourceHelper {
auto readFromDisk(const std::string& relative_path) -> std::vector<uint8_t> {
const std::string full = std::string(file_getresourcefolder()) + relative_path;
std::ifstream file(full, std::ios::binary | std::ios::ate);
if (!file) return {};
if (!file) {
return {};
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(size);
if (!file.read(reinterpret_cast<char*>(data.data()), size)) return {};
if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
return {};
}
return data;
}
} // namespace
+15 -5
View File
@@ -95,11 +95,21 @@ namespace Resource {
}
auto List::parseAssetType(const std::string& type_str) -> Type {
if (type_str == "DATA") return Type::DATA;
if (type_str == "BITMAP") return Type::BITMAP;
if (type_str == "MUSIC") return Type::MUSIC;
if (type_str == "SOUND") return Type::SOUND;
if (type_str == "FONT") return Type::FONT;
if (type_str == "DATA") {
return Type::DATA;
}
if (type_str == "BITMAP") {
return Type::BITMAP;
}
if (type_str == "MUSIC") {
return Type::MUSIC;
}
if (type_str == "SOUND") {
return Type::SOUND;
}
if (type_str == "FONT") {
return Type::FONT;
}
throw std::runtime_error("Unknown asset type: " + type_str);
}
+9 -3
View File
@@ -29,7 +29,9 @@ auto ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32
}
void ResourcePack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
if (key.empty()) return;
if (key.empty()) {
return;
}
for (size_t i = 0; i < data.size(); ++i) {
data[i] ^= static_cast<uint8_t>(key[i % key.length()]);
}
@@ -162,7 +164,9 @@ auto ResourcePack::addDirectory(const std::string& directory) -> bool {
}
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
if (!entry.is_regular_file()) continue;
if (!entry.is_regular_file()) {
continue;
}
std::string filepath = entry.path().string();
std::string filename = std::filesystem::relative(entry.path(), directory).string();
@@ -177,7 +181,9 @@ auto ResourcePack::addDirectory(const std::string& directory) -> bool {
auto ResourcePack::getResource(const std::string& filename) -> std::vector<uint8_t> {
auto it = resources_.find(filename);
if (it == resources_.end()) return {};
if (it == resources_.end()) {
return {};
}
const ResourceEntry& entry = it->second;
if (entry.offset + entry.size > data_.size()) {
+19 -15
View File
@@ -56,7 +56,7 @@ void Director::initGameContext() {
}
}
std::unique_ptr<scenes::Scene> Director::createNextScene() {
auto Director::createNextScene() const -> std::unique_ptr<scenes::Scene> {
// Mentre el Resource::Cache no haja acabat de precarregar, executem
// el BootLoaderScene — pinta una barra de progrés i avança la
// càrrega per pressupost de temps. Quan acaba, retorna i tornem ací
@@ -139,7 +139,7 @@ void Director::setup() {
has_frame_ = false;
}
bool Director::iterate() {
auto Director::iterate() -> bool {
if (quit_requested_) {
JG_QuitSignal();
current_scene_.reset(); // destrueix l'escena actual ordenadament
@@ -216,9 +216,13 @@ bool Director::iterate() {
// game_state_ i info::ctx. Si és impossible (game_state_ == -1,
// quit, o state no registrat), eixim del loop.
if (!current_scene_) {
if (game_state_ == -1 || JG_Quitting()) return false;
if (game_state_ == -1 || JG_Quitting()) {
return false;
}
current_scene_ = createNextScene();
if (!current_scene_) return false;
if (!current_scene_) {
return false;
}
current_scene_->onEnter();
last_tick_ms_ = SDL_GetTicks();
}
@@ -270,7 +274,9 @@ void Director::run() {
setup();
while (true) {
pollAllEvents();
if (!iterate()) break;
if (!iterate()) {
break;
}
}
teardown();
}
@@ -379,16 +385,14 @@ void Director::handleEvent(const SDL_Event& event) {
// Ja processat a KEY_DOWN, només deixem netejar el bloqueig
// quan l'overlay faça timeout
return;
} else {
// Comprova si és una tecla d'UI registrada (no passa al joc).
// KeyConfig::isGuiKey cobreix totes les tecles GUI a la vegada,
// incloent pause_toggle i menu_toggle (defensa en profunditat:
// aquestes ja s'haurien hagut de menjar al swallow d'amunt).
const auto sc = event.key.scancode;
if (!KeyConfig::isGuiKey(sc)) {
key_pressed_ = true;
JI_moveCheats(sc);
}
} // Comprova si és una tecla d'UI registrada (no passa al joc).
// KeyConfig::isGuiKey cobreix totes les tecles GUI a la vegada,
// incloent pause_toggle i menu_toggle (defensa en profunditat:
// aquestes ja s'haurien hagut de menjar al swallow d'amunt).
const auto sc = event.key.scancode;
if (!KeyConfig::isGuiKey(sc)) {
key_pressed_ = true;
JI_moveCheats(sc);
}
}
Mouse::handleEvent(event);
+6 -7
View File
@@ -28,13 +28,13 @@ class Director {
// per l'event loop de SDL3 en lloc d'un bucle propi — imprescindible
// per al port a emscripten, on el runtime posseïx el main loop.
void setup();
bool iterate(); // torna false quan el joc vol eixir
auto iterate() -> bool; // torna false quan el joc vol eixir
void teardown();
void handleEvent(const SDL_Event& event);
// Demana l'eixida (ex: segona pulsació d'ESC o SDL_QUIT)
void requestQuit();
auto isQuitRequested() const -> bool { return quit_requested_; }
[[nodiscard]] auto isQuitRequested() const -> bool { return quit_requested_; }
// Demana un reinici "suau": para música i sons, reseteja info::ctx i
// torna a l'intro (state 255). Es processa al començament del pròxim
@@ -45,14 +45,13 @@ class Director {
auto consumeKeyPressed() -> bool;
// Indica si ESC està bloquejada (el joc no l'ha de veure)
auto isEscBlocked() const -> bool { return esc_blocked_ || esc_swallow_until_release_; }
[[nodiscard]] auto isEscBlocked() const -> bool { return esc_blocked_ || esc_swallow_until_release_; }
// Pausa: mentre està activa, iterate() no avança l'escena — es
// continua presentant el darrer frame amb overlay fresc.
void togglePause();
auto isPaused() const -> bool { return paused_; }
[[nodiscard]] auto isPaused() const -> bool { return paused_; }
public:
~Director();
private:
@@ -64,10 +63,10 @@ class Director {
// Inicialitza info::ctx a partir de Options::game.* i comprova trick.ini.
// Es crida una sola vegada des d'iterate() a la primera invocació.
void initGameContext();
static void initGameContext();
// Construeix l'escena apropiada segons game_state_ i info::ctx.
// Retorna nullptr si l'state actual no té escena registrada (bug).
std::unique_ptr<scenes::Scene> createNextScene();
auto createNextScene() const -> std::unique_ptr<scenes::Scene>;
// Buffers persistents entre iteracions. Abans eren locals a run(),
// ara són membres perquè iterate() els pot reutilitzar sense tornar-los
+16 -6
View File
@@ -1,6 +1,6 @@
#include "game/bola.hpp"
#include <stdlib.h>
#include <cstdlib>
#include "core/jail/jgame.hpp"
@@ -24,29 +24,39 @@ Bola::Bola(JD8_Surface gfx, Prota* sam)
}
void Bola::draw() {
if (this->contador == 0) Sprite::draw();
if (this->contador == 0) {
Sprite::draw();
}
}
void Bola::update() {
if (this->contador == 0) {
// Augmentem la x
this->x++;
if (this->x == 280) this->contador = 200;
if (this->x == 280) {
this->contador = 200;
}
// Augmentem el frame
if (JG_GetCycleCounter() % this->cycles_per_frame == 0) {
this->cur_frame++;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) this->cur_frame = 0;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) {
this->cur_frame = 0;
}
}
// Comprovem si ha tocat a Sam
if (this->x > (this->sam->x - 7) && this->x < (this->sam->x + 7) && this->y > (this->sam->y - 7) && this->y < (this->sam->y + 7)) {
this->contador = 200;
info::ctx.vida--;
if (info::ctx.vida == 0) this->sam->o = 5;
if (info::ctx.vida == 0) {
this->sam->o = 5;
}
}
} else {
this->contador--;
if (this->contador == 0) this->x = 20;
if (this->contador == 0) {
this->x = 20;
}
}
}
+8 -4
View File
@@ -1,6 +1,6 @@
#include "game/engendro.hpp"
#include <stdlib.h>
#include <cstdlib>
#include "core/jail/jgame.hpp"
@@ -29,16 +29,20 @@ Engendro::Engendro(JD8_Surface gfx, Uint16 x, Uint16 y)
this->cycles_per_frame = 30;
}
bool Engendro::update() {
auto Engendro::update() -> bool {
bool mort = false;
if (JG_GetCycleCounter() % this->cycles_per_frame == 0) {
this->cur_frame++;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) this->cur_frame = 0;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) {
this->cur_frame = 0;
}
this->vida--;
}
if (vida == 0) mort = true;
if (vida == 0) {
mort = true;
}
return mort;
}
+1 -1
View File
@@ -6,7 +6,7 @@ class Engendro : public Sprite {
public:
explicit Engendro(JD8_Surface gfx, Uint16 x, Uint16 y);
bool update();
auto update() -> bool;
protected:
Uint8 vida;
+44 -25
View File
@@ -1,6 +1,6 @@
#include "game/mapa.hpp"
#include <stdlib.h>
#include <cstdlib>
#include "core/jail/jgame.hpp"
#include "core/jail/jinput.hpp"
@@ -21,7 +21,7 @@ Mapa::Mapa(JD8_Surface gfx, Prota* sam) {
this->nova_momia = false;
}
Mapa::~Mapa(void) {
Mapa::~Mapa() {
JD8_FreeSurface(this->fondo);
}
@@ -48,7 +48,7 @@ void Mapa::draw() {
// Pinta tombes
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
JD8_BlitCK(35 + (x * 65), 45 + (y * 35), this->gfx, this->tombes[x + y * 4].x, this->tombes[x + y * 4].y, 50, 20, 255);
JD8_BlitCK(35 + (x * 65), 45 + (y * 35), this->gfx, this->tombes[x + (y * 4)].x, this->tombes[x + (y * 4)].y, 50, 20, 255);
}
}
@@ -62,7 +62,9 @@ void Mapa::update() {
if (((sam->x - 20) % 65 == 0) && ((sam->y - 30) % 35 == 0) && ((this->ultim_vertex.columna != (sam->x - 20) / 65) || (this->ultim_vertex.fila != (sam->y - 30) / 35))) {
this->vertex.columna = (sam->x - 20) / 65;
this->vertex.fila = (sam->y - 30) / 35;
if (this->ultim_vertex.columna != 255) this->comprovaUltimCami();
if (this->ultim_vertex.columna != 255) {
this->comprovaUltimCami();
}
this->ultim_vertex = this->vertex;
}
@@ -79,7 +81,7 @@ void Mapa::update() {
}
}
bool Mapa::novaMomia() {
auto Mapa::novaMomia() -> bool {
bool resultat = nova_momia;
nova_momia = false;
return resultat;
@@ -96,7 +98,9 @@ void Mapa::preparaFondoEstatic() {
}
JD8_BlitToSurface(130, 2, this->gfx, 225, 192, 19, 8, this->fondo); // Montonet de monedes + signe '='
JD8_BlitToSurface(220, 2, this->gfx, 160, 185, 48, 7, this->fondo); // Text "ENERGIA"
if (info::ctx.diners >= 200) JD8_BlitToSurface(175, 3, this->gfx, 60, 193, 7, 6, this->fondo);
if (info::ctx.diners >= 200) {
JD8_BlitToSurface(175, 3, this->gfx, 60, 193, 7, 6, this->fondo);
}
// Pinta taulells
for (int y = 0; y < 11; y++) {
@@ -150,7 +154,7 @@ void Mapa::preparaFondoEstatic() {
}
}
void swap(Uint8& a, Uint8& b) {
void swap(Uint8& a, Uint8& b) noexcept {
Uint8 temp = a;
a = b;
b = temp;
@@ -161,28 +165,36 @@ void Mapa::preparaTombes() {
int cx = info::ctx.num_piramide == 6 ? 270 : 0;
int cy = info::ctx.num_piramide == 6 ? 50 : 0;
for (int i = 0; i < 16; i++) {
this->tombes[i].contingut = contingut;
this->tombes[i].oberta = false;
this->tombes[i].costat[0] = false;
this->tombes[i].costat[1] = false;
this->tombes[i].costat[2] = false;
this->tombes[i].costat[3] = false;
this->tombes[i].x = cx;
this->tombes[i].y = cy;
for (auto& tombe : this->tombes) {
tombe.contingut = contingut;
tombe.oberta = false;
tombe.costat[0] = false;
tombe.costat[1] = false;
tombe.costat[2] = false;
tombe.costat[3] = false;
tombe.x = cx;
tombe.y = cy;
}
if (info::ctx.num_piramide == 6) {
return;
}
if (info::ctx.num_piramide == 6) return;
this->tombes[0].contingut = CONTE_FARAO;
this->tombes[1].contingut = CONTE_CLAU;
this->tombes[2].contingut = CONTE_PERGAMI;
this->tombes[3].contingut = CONTE_MOMIA;
for (int i = 4; i < 8; i++) this->tombes[i].contingut = CONTE_RES;
for (int i = 8; i < 16; i++) this->tombes[i].contingut = CONTE_TRESOR;
for (int i = 4; i < 8; i++) {
this->tombes[i].contingut = CONTE_RES;
}
for (int i = 8; i < 16; i++) {
this->tombes[i].contingut = CONTE_TRESOR;
}
for (int i = 0; i < 50; i++) swap(this->tombes[rand() % 16].contingut, this->tombes[rand() % 16].contingut);
for (int i = 0; i < 50; i++) {
swap(this->tombes[rand() % 16].contingut, this->tombes[rand() % 16].contingut);
}
}
Uint8 minim(Uint8 a, Uint8 b) {
auto minim(Uint8 a, Uint8 b) -> Uint8 {
return (a < b) ? a : b;
}
@@ -225,11 +237,16 @@ void Mapa::comprovaUltimCami() {
void Mapa::comprovaCaixa(Uint8 num) {
// Si la tomba ja està oberta, no hi ha res que mirar
if (this->tombes[num].oberta) return;
if (this->tombes[num].oberta) {
return;
}
// Si algun costat encara no està passat, no hi ha res que fer
for (int i = 0; i < 4; i++)
if (!this->tombes[num].costat[i]) return;
for (bool i : this->tombes[num].costat) {
if (!i) {
return;
}
}
// Sinó, pos la acabem d'obrir
this->tombes[num].oberta = true;
@@ -263,7 +280,9 @@ void Mapa::comprovaCaixa(Uint8 num) {
this->tombes[num].y = 70;
info::ctx.diamants++;
info::ctx.diners += VALOR_DIAMANT;
if (info::ctx.diamants == 16) this->farao = this->clau = true;
if (info::ctx.diamants == 16) {
this->farao = this->clau = true;
}
break;
}
+14 -12
View File
@@ -4,14 +4,16 @@
#include "game/info.hpp"
#include "game/prota.hpp"
#define CONTE_RES 0
#define CONTE_TRESOR 1
#define CONTE_FARAO 2
#define CONTE_CLAU 3
#define CONTE_MOMIA 4
#define CONTE_PERGAMI 5
#define CONTE_DIAMANT 6
#define VALOR_DIAMANT 5
enum {
CONTE_RES = 0,
CONTE_TRESOR = 1,
CONTE_FARAO = 2,
CONTE_CLAU = 3,
CONTE_MOMIA = 4,
CONTE_PERGAMI = 5,
CONTE_DIAMANT = 6,
VALOR_DIAMANT = 5
};
struct Tomba {
bool costat[4];
@@ -28,16 +30,16 @@ struct Vertex {
class Mapa {
public:
explicit Mapa(JD8_Surface gfx, Prota* sam);
~Mapa(void);
~Mapa();
Mapa(const Mapa&) = delete;
Mapa& operator=(const Mapa&) = delete;
auto operator=(const Mapa&) -> Mapa& = delete;
Mapa(Mapa&&) = delete;
Mapa& operator=(Mapa&&) = delete;
auto operator=(Mapa&&) -> Mapa& = delete;
void draw();
void update();
bool novaMomia();
auto novaMomia() -> bool;
void comprovaCaixa(Uint8 num);
Tomba tombes[16];
+7 -4
View File
@@ -5,8 +5,7 @@ Marcador::Marcador(JD8_Surface gfx, Prota* sam) {
this->sam = sam;
}
Marcador::~Marcador(void) {
}
Marcador::~Marcador() = default;
void Marcador::draw() {
if (info::ctx.num_piramide < 6) {
@@ -18,10 +17,14 @@ void Marcador::draw() {
this->pintaNumero(156, 2, (info::ctx.diners % 100) / 10);
this->pintaNumero(163, 2, info::ctx.diners % 10);
if (this->sam->pergami) JD8_BlitCK(190, 1, this->gfx, 209, 185, 15, 14, 255);
if (this->sam->pergami) {
JD8_BlitCK(190, 1, this->gfx, 209, 185, 15, 14, 255);
}
JD8_BlitCK(271, 1, this->gfx, 0, 20, 15, info::ctx.vida * 3, 255);
if (info::ctx.vida < 5) JD8_BlitCK(271, 1 + (info::ctx.vida * 3), this->gfx, 75, 20, 15, 15 - (info::ctx.vida * 3), 255);
if (info::ctx.vida < 5) {
JD8_BlitCK(271, 1 + (info::ctx.vida * 3), this->gfx, 75, 20, 15, 15 - (info::ctx.vida * 3), 255);
}
}
void Marcador::pintaNumero(Uint16 x, Uint16 y, Uint8 num) {
+1 -1
View File
@@ -7,7 +7,7 @@
class Marcador {
public:
explicit Marcador(JD8_Surface gfx, Prota* sam);
~Marcador(void);
~Marcador();
void draw();
+29 -11
View File
@@ -54,7 +54,9 @@ void ModuleGame::tick(int delta_ms) {
// per la Draw() d'onEnter. Només el JD8_Flip del caller muta
// pixel_data segons la paleta que avança pas a pas.
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Playing;
if (fade_.done()) {
phase_ = Phase::Playing;
}
break;
case Phase::Playing:
@@ -75,7 +77,9 @@ void ModuleGame::tick(int delta_ms) {
// mostraria l'estat post-Update del sprite (p.ex. el prota
// "tornant" davant la porta després d'haver eixit).
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Done;
if (fade_.done()) {
phase_ = Phase::Done;
}
break;
case Phase::Done:
@@ -83,8 +87,10 @@ void ModuleGame::tick(int delta_ms) {
}
}
int ModuleGame::nextState() const {
if (JG_Quitting()) return -1;
auto ModuleGame::nextState() const -> int {
if (JG_Quitting()) {
return -1;
}
if (info::ctx.num_habitacio == 1 ||
info::ctx.num_piramide == 100 ||
info::ctx.num_piramide == 7) {
@@ -93,14 +99,16 @@ int ModuleGame::nextState() const {
return 0;
}
void ModuleGame::applyFinalTransitions() {
void ModuleGame::applyFinalTransitions() const {
if (this->final_ == 1) {
info::ctx.num_habitacio++;
if (info::ctx.num_habitacio == 6) {
info::ctx.num_habitacio = 1;
info::ctx.num_piramide++;
}
if (info::ctx.num_piramide == 6 && info::ctx.num_habitacio == 2) info::ctx.num_piramide++;
if (info::ctx.num_piramide == 6 && info::ctx.num_habitacio == 2) {
info::ctx.num_piramide++;
}
} else if (this->final_ == 2) {
info::ctx.num_piramide = 100;
}
@@ -112,8 +120,12 @@ void ModuleGame::Draw() {
this->mapa->draw();
this->marcador->draw();
this->sam->draw();
for (auto& m : this->momies) m->draw();
if (this->bola) this->bola->draw();
for (auto& m : this->momies) {
m->draw();
}
if (this->bola) {
this->bola->draw();
}
}
void ModuleGame::Update() {
@@ -123,14 +135,18 @@ void ModuleGame::Update() {
this->final_ = this->sam->update();
const auto erased = std::erase_if(this->momies, [](auto& m) { return m->update(); });
info::ctx.momies -= static_cast<int>(erased);
if (this->bola) this->bola->update();
if (this->bola) {
this->bola->update();
}
this->mapa->update();
if (this->mapa->novaMomia()) {
this->momies.emplace_back(std::make_unique<Momia>(this->gfx, true, 0, 0, this->sam.get()));
info::ctx.momies++;
}
if (JI_CheatActivated("reviu")) info::ctx.vida = 5;
if (JI_CheatActivated("reviu")) {
info::ctx.vida = 5;
}
if (JI_CheatActivated("alone")) {
this->momies.clear();
info::ctx.momies = 0;
@@ -157,7 +173,9 @@ void ModuleGame::iniciarMomies() {
} else {
info::ctx.momies++;
}
if (info::ctx.num_piramide == 6) info::ctx.momies = 8;
if (info::ctx.num_piramide == 6) {
info::ctx.momies = 8;
}
int x = 20;
int y = 170;
+5 -5
View File
@@ -32,14 +32,14 @@ class ModuleGame : public scenes::Scene {
~ModuleGame() override;
ModuleGame(const ModuleGame&) = delete;
ModuleGame& operator=(const ModuleGame&) = delete;
auto operator=(const ModuleGame&) -> ModuleGame& = delete;
ModuleGame(ModuleGame&&) = delete;
ModuleGame& operator=(ModuleGame&&) = delete;
auto operator=(ModuleGame&&) -> ModuleGame& = delete;
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override;
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
[[nodiscard]] auto nextState() const -> int override;
private:
enum class Phase {
@@ -53,7 +53,7 @@ class ModuleGame : public scenes::Scene {
void Update(); // gated per JG_ShouldUpdate
void iniciarMomies();
void applyFinalTransitions(); // muta info::ctx quan final_ passa a !=0
void applyFinalTransitions() const; // muta info::ctx quan final_ passa a !=0
Phase phase_{Phase::FadingIn};
scenes::PaletteFade fade_;
+34 -18
View File
@@ -1,6 +1,6 @@
#include "game/momia.hpp"
#include <stdlib.h>
#include <cstdlib>
#include "core/jail/jgame.hpp"
@@ -15,9 +15,13 @@ Momia::Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam)
Frame f;
f.w = 15;
f.h = 15;
if (info::ctx.num_piramide == 4) f.h -= 5;
if (info::ctx.num_piramide == 4) {
f.h -= 5;
}
f.x = (col * 15) + 75;
if (this->dimoni) f.x += 75;
if (this->dimoni) {
f.x += 75;
}
f.y = 20 + (row * 15);
entitat.frames.push_back(f);
}
@@ -26,14 +30,14 @@ Momia::Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam)
entitat.animacions.resize(4);
for (int i = 0; i < 4; i++) {
entitat.animacions[i].frames = {
static_cast<Uint8>(0 + i * 5),
static_cast<Uint8>(1 + i * 5),
static_cast<Uint8>(2 + i * 5),
static_cast<Uint8>(1 + i * 5),
static_cast<Uint8>(0 + i * 5),
static_cast<Uint8>(3 + i * 5),
static_cast<Uint8>(4 + i * 5),
static_cast<Uint8>(3 + i * 5),
static_cast<Uint8>(0 + (i * 5)),
static_cast<Uint8>(1 + (i * 5)),
static_cast<Uint8>(2 + (i * 5)),
static_cast<Uint8>(1 + (i * 5)),
static_cast<Uint8>(0 + (i * 5)),
static_cast<Uint8>(3 + (i * 5)),
static_cast<Uint8>(4 + (i * 5)),
static_cast<Uint8>(3 + (i * 5)),
};
}
@@ -79,7 +83,7 @@ void Momia::draw() {
}
}
bool Momia::update() {
auto Momia::update() -> bool {
bool morta = false;
if (this->engendro) {
@@ -120,22 +124,32 @@ bool Momia::update() {
switch (this->o) {
case 0:
if (y < 170) this->y++;
if (y < 170) {
this->y++;
}
break;
case 1:
if (y > 30) this->y--;
if (y > 30) {
this->y--;
}
break;
case 2:
if (x < 280) this->x++;
if (x < 280) {
this->x++;
}
break;
case 3:
if (x > 20) this->x--;
if (x > 20) {
this->x--;
}
break;
}
if (JG_GetCycleCounter() % this->cycles_per_frame == 0) {
this->cur_frame++;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) this->cur_frame = 0;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) {
this->cur_frame = 0;
}
}
if (this->x > (this->sam->x - 7) && this->x < (this->sam->x + 7) && this->y > (this->sam->y - 7) && this->y < (this->sam->y + 7)) {
@@ -144,7 +158,9 @@ bool Momia::update() {
this->sam->pergami = false;
} else {
info::ctx.vida--;
if (info::ctx.vida == 0) this->sam->o = 5;
if (info::ctx.vida == 0) {
this->sam->o = 5;
}
}
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ class Momia : public Sprite {
explicit Momia(JD8_Surface gfx, bool dimoni, Uint16 x, Uint16 y, Prota* sam);
void draw() override;
bool update();
auto update() -> bool;
bool dimoni;
+119 -55
View File
@@ -1,5 +1,6 @@
#include "game/options.hpp"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
@@ -58,16 +59,21 @@ namespace Options {
auto yaml = fkyaml::node::deserialize(content);
if (yaml.contains("game")) {
const auto& node = yaml["game"];
if (node.contains("habitacio_inicial"))
if (node.contains("habitacio_inicial")) {
game.habitacio_inicial = node["habitacio_inicial"].get_value<int>();
if (node.contains("piramide_inicial"))
}
if (node.contains("piramide_inicial")) {
game.piramide_inicial = node["piramide_inicial"].get_value<int>();
if (node.contains("vides"))
}
if (node.contains("vides")) {
game.vides = node["vides"].get_value<int>();
if (node.contains("diamants_inicial"))
}
if (node.contains("diamants_inicial")) {
game.diamants_inicial = node["diamants_inicial"].get_value<int>();
if (node.contains("diners_inicial"))
}
if (node.contains("diners_inicial")) {
game.diners_inicial = node["diners_inicial"].get_value<int>();
}
}
return true;
} catch (const fkyaml::exception& e) {
@@ -80,7 +86,9 @@ namespace Options {
// com a punt d'entrada únic per als callsites legacy del menú; el cos
// ja no toca jail_audio directament.
void applyAudio() {
if (::Audio::get() == nullptr) return;
if (::Audio::get() == nullptr) {
return;
}
::Audio::get()->enable(audio.enabled);
::Audio::get()->enableMusic(audio.music.enabled);
::Audio::get()->enableSound(audio.sound.enabled);
@@ -91,114 +99,148 @@ namespace Options {
// --- Funcions helper de càrrega ---
static void loadAudioConfigFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("audio")) return;
if (!yaml.contains("audio")) {
return;
}
const auto& node = yaml["audio"];
if (node.contains("enabled"))
if (node.contains("enabled")) {
audio.enabled = node["enabled"].get_value<bool>();
}
if (node.contains("volume"))
if (node.contains("volume")) {
audio.volume = node["volume"].get_value<float>();
}
if (node.contains("music")) {
const auto& music = node["music"];
if (music.contains("enabled"))
if (music.contains("enabled")) {
audio.music.enabled = music["enabled"].get_value<bool>();
if (music.contains("volume"))
}
if (music.contains("volume")) {
audio.music.volume = music["volume"].get_value<float>();
}
}
if (node.contains("sound")) {
const auto& sound = node["sound"];
if (sound.contains("enabled"))
if (sound.contains("enabled")) {
audio.sound.enabled = sound["enabled"].get_value<bool>();
if (sound.contains("volume"))
}
if (sound.contains("volume")) {
audio.sound.volume = sound["volume"].get_value<float>();
}
}
}
static void loadVideoConfigFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("video")) return;
if (!yaml.contains("video")) {
return;
}
const auto& node = yaml["video"];
if (node.contains("gpu_acceleration"))
if (node.contains("gpu_acceleration")) {
video.gpu_acceleration = node["gpu_acceleration"].get_value<bool>();
if (node.contains("shader_enabled"))
}
if (node.contains("shader_enabled")) {
video.shader_enabled = node["shader_enabled"].get_value<bool>();
if (node.contains("supersampling"))
}
if (node.contains("supersampling")) {
video.supersampling = node["supersampling"].get_value<bool>();
}
if (node.contains("scaling_mode")) {
auto s = node["scaling_mode"].get_value<std::string>();
if (s == "disabled")
if (s == "disabled") {
video.scaling_mode = ScalingMode::DISABLED;
else if (s == "stretch")
} else if (s == "stretch") {
video.scaling_mode = ScalingMode::STRETCH;
else if (s == "letterbox")
} else if (s == "letterbox") {
video.scaling_mode = ScalingMode::LETTERBOX;
else if (s == "overscan")
} else if (s == "overscan") {
video.scaling_mode = ScalingMode::OVERSCAN;
else
} else {
video.scaling_mode = ScalingMode::INTEGER;
}
}
if (node.contains("vsync"))
if (node.contains("vsync")) {
video.vsync = node["vsync"].get_value<bool>();
if (node.contains("aspect_ratio_4_3"))
}
if (node.contains("aspect_ratio_4_3")) {
video.aspect_ratio_4_3 = node["aspect_ratio_4_3"].get_value<bool>();
}
if (node.contains("texture_filter")) {
auto s = node["texture_filter"].get_value<std::string>();
video.texture_filter = (s == "linear") ? TextureFilter::LINEAR : TextureFilter::NEAREST;
}
if (node.contains("downscale_algo"))
if (node.contains("downscale_algo")) {
video.downscale_algo = node["downscale_algo"].get_value<int>();
}
if (node.contains("internal_resolution")) {
video.internal_resolution = node["internal_resolution"].get_value<int>();
if (video.internal_resolution < 1) video.internal_resolution = 1;
video.internal_resolution = std::max(video.internal_resolution, 1);
}
if (node.contains("current_shader"))
if (node.contains("current_shader")) {
video.current_shader = node["current_shader"].get_value<std::string>();
if (node.contains("current_postfx_preset"))
}
if (node.contains("current_postfx_preset")) {
video.current_postfx_preset = node["current_postfx_preset"].get_value<std::string>();
if (node.contains("current_crtpi_preset"))
}
if (node.contains("current_crtpi_preset")) {
video.current_crtpi_preset = node["current_crtpi_preset"].get_value<std::string>();
}
}
static void loadRenderInfoFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("render_info")) return;
if (!yaml.contains("render_info")) {
return;
}
const auto& node = yaml["render_info"];
if (node.contains("position")) {
auto pos = node["position"].get_value<std::string>();
if (pos == "top")
if (pos == "top") {
render_info.position = RenderInfoPosition::TOP;
else if (pos == "bottom")
} else if (pos == "bottom") {
render_info.position = RenderInfoPosition::BOTTOM;
else
} else {
render_info.position = RenderInfoPosition::OFF;
}
}
if (node.contains("show_time"))
if (node.contains("show_time")) {
render_info.show_time = node["show_time"].get_value<bool>();
if (node.contains("text_color"))
}
if (node.contains("text_color")) {
render_info.text_color = static_cast<Uint32>(node["text_color"].get_value<uint64_t>());
if (node.contains("shadow_color"))
}
if (node.contains("shadow_color")) {
render_info.shadow_color = static_cast<Uint32>(node["shadow_color"].get_value<uint64_t>());
}
}
static void loadWindowConfigFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("window")) return;
if (!yaml.contains("window")) {
return;
}
const auto& node = yaml["window"];
if (node.contains("zoom"))
if (node.contains("zoom")) {
window.zoom = node["zoom"].get_value<int>();
if (node.contains("fullscreen"))
}
if (node.contains("fullscreen")) {
window.fullscreen = node["fullscreen"].get_value<bool>();
}
}
// Helper: carrega una SDL_Scancode des d'un string (nom SDL de la tecla).
static void loadScancodeField(const fkyaml::node& node, const std::string& key, SDL_Scancode& target) {
if (!node.contains(key)) return;
if (!node.contains(key)) {
return;
}
auto name = node[key].get_value<std::string>();
SDL_Scancode sc = SDL_GetScancodeFromName(name.c_str());
if (sc != SDL_SCANCODE_UNKNOWN) target = sc;
if (sc != SDL_SCANCODE_UNKNOWN) {
target = sc;
}
}
static void loadControlsFromYaml(const fkyaml::node& yaml) {
@@ -212,15 +254,20 @@ namespace Options {
}
static void loadGameConfigFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("game")) return;
if (!yaml.contains("game")) {
return;
}
const auto& node = yaml["game"];
if (node.contains("use_new_logo"))
if (node.contains("use_new_logo")) {
game.use_new_logo = node["use_new_logo"].get_value<bool>();
if (node.contains("show_title_credits"))
}
if (node.contains("show_title_credits")) {
game.show_title_credits = node["show_title_credits"].get_value<bool>();
if (node.contains("show_preload"))
}
if (node.contains("show_preload")) {
game.show_preload = node["show_preload"].get_value<bool>();
}
}
// Carrega les opcions des del fitxer configurat
@@ -334,10 +381,11 @@ namespace Options {
file << "render_info:\n";
{
const char* pos = "off";
if (render_info.position == RenderInfoPosition::TOP)
if (render_info.position == RenderInfoPosition::TOP) {
pos = "top";
else if (render_info.position == RenderInfoPosition::BOTTOM)
} else if (render_info.position == RenderInfoPosition::BOTTOM) {
pos = "bottom";
}
file << " position: " << pos << " # off/top/bottom\n";
}
file << " show_time: " << (render_info.show_time ? "true" : "false") << "\n";
@@ -438,7 +486,9 @@ namespace Options {
if (yaml.contains("presets")) {
for (const auto& p : yaml["presets"]) {
PostFXPreset preset;
if (p.contains("name")) preset.name = p["name"].get_value<std::string>();
if (p.contains("name")) {
preset.name = p["name"].get_value<std::string>();
}
parseFloatField(p, "vignette", preset.vignette);
parseFloatField(p, "scanlines", preset.scanlines);
parseFloatField(p, "chroma", preset.chroma);
@@ -497,7 +547,9 @@ namespace Options {
if (yaml.contains("presets")) {
for (const auto& p : yaml["presets"]) {
CrtPiPreset preset;
if (p.contains("name")) preset.name = p["name"].get_value<std::string>();
if (p.contains("name")) {
preset.name = p["name"].get_value<std::string>();
}
parseFloatField(p, "scanline_weight", preset.scanline_weight);
parseFloatField(p, "scanline_gap_brightness", preset.scanline_gap_brightness);
parseFloatField(p, "bloom_factor", preset.bloom_factor);
@@ -506,24 +558,36 @@ namespace Options {
parseFloatField(p, "mask_brightness", preset.mask_brightness);
parseFloatField(p, "curvature_x", preset.curvature_x);
parseFloatField(p, "curvature_y", preset.curvature_y);
if (p.contains("mask_type")) try {
if (p.contains("mask_type")) {
try {
preset.mask_type = p["mask_type"].get_value<int>();
} catch (...) {}
if (p.contains("enable_scanlines")) try {
}
if (p.contains("enable_scanlines")) {
try {
preset.enable_scanlines = p["enable_scanlines"].get_value<bool>();
} catch (...) {}
if (p.contains("enable_multisample")) try {
}
if (p.contains("enable_multisample")) {
try {
preset.enable_multisample = p["enable_multisample"].get_value<bool>();
} catch (...) {}
if (p.contains("enable_gamma")) try {
}
if (p.contains("enable_gamma")) {
try {
preset.enable_gamma = p["enable_gamma"].get_value<bool>();
} catch (...) {}
if (p.contains("enable_curvature")) try {
}
if (p.contains("enable_curvature")) {
try {
preset.enable_curvature = p["enable_curvature"].get_value<bool>();
} catch (...) {}
if (p.contains("enable_sharper")) try {
}
if (p.contains("enable_sharper")) {
try {
preset.enable_sharper = p["enable_sharper"].get_value<bool>();
} catch (...) {}
}
crtpi_presets.push_back(preset);
}
}
+59 -27
View File
@@ -1,6 +1,6 @@
#include "game/prota.hpp"
#include <stdlib.h>
#include <cstdlib>
#include "core/jail/jgame.hpp"
#include "core/jail/jinput.hpp"
@@ -14,7 +14,9 @@ Prota::Prota(JD8_Surface gfx)
Frame f;
f.w = 15;
f.h = 15;
if (info::ctx.num_piramide == 4) f.h -= 5;
if (info::ctx.num_piramide == 4) {
f.h -= 5;
}
f.x = x * 15;
f.y = 20 + (y * 15);
entitat.frames.push_back(f);
@@ -26,7 +28,9 @@ Prota::Prota(JD8_Surface gfx)
Frame f;
f.w = 15;
f.h = 30;
if (info::ctx.num_piramide == 4) f.h -= 5;
if (info::ctx.num_piramide == 4) {
f.h -= 5;
}
f.x = x;
f.y = y;
entitat.frames.push_back(f);
@@ -38,7 +42,9 @@ Prota::Prota(JD8_Surface gfx)
Frame f;
f.w = 15;
f.h = 15;
if (info::ctx.num_piramide == 4) f.h -= 5;
if (info::ctx.num_piramide == 4) {
f.h -= 5;
}
f.x = x;
f.y = y;
entitat.frames.push_back(f);
@@ -48,23 +54,29 @@ Prota::Prota(JD8_Surface gfx)
entitat.animacions.resize(6);
for (int i = 0; i < 4; i++) {
entitat.animacions[i].frames = {
static_cast<Uint8>(0 + i * 5),
static_cast<Uint8>(1 + i * 5),
static_cast<Uint8>(2 + i * 5),
static_cast<Uint8>(1 + i * 5),
static_cast<Uint8>(0 + i * 5),
static_cast<Uint8>(3 + i * 5),
static_cast<Uint8>(4 + i * 5),
static_cast<Uint8>(3 + i * 5),
static_cast<Uint8>(0 + (i * 5)),
static_cast<Uint8>(1 + (i * 5)),
static_cast<Uint8>(2 + (i * 5)),
static_cast<Uint8>(1 + (i * 5)),
static_cast<Uint8>(0 + (i * 5)),
static_cast<Uint8>(3 + (i * 5)),
static_cast<Uint8>(4 + (i * 5)),
static_cast<Uint8>(3 + (i * 5)),
};
}
entitat.animacions[4].frames.resize(50);
for (int i = 0; i < 50; i++) entitat.animacions[4].frames[i] = i + 20;
for (int i = 0; i < 50; i++) {
entitat.animacions[4].frames[i] = i + 20;
}
entitat.animacions[5].frames.resize(48);
for (int i = 0; i < 12; i++) entitat.animacions[5].frames[i] = i + 70;
for (int i = 12; i < 48; i++) entitat.animacions[5].frames[i] = 81;
for (int i = 0; i < 12; i++) {
entitat.animacions[5].frames[i] = i + 70;
}
for (int i = 12; i < 48; i++) {
entitat.animacions[5].frames[i] = 81;
}
cur_frame = 0;
x = 150;
@@ -87,40 +99,56 @@ void Prota::draw() {
}
}
Uint8 Prota::update() {
auto Prota::update() -> Uint8 {
Uint8 eixir = 0;
if (this->o < 4) {
Uint8 dir = 4;
if (JI_KeyPressed(SDL_SCANCODE_DOWN)) {
if ((this->x - 20) % 65 == 0) this->o = 0;
if ((this->x - 20) % 65 == 0) {
this->o = 0;
}
dir = this->o;
}
if (JI_KeyPressed(SDL_SCANCODE_UP)) {
if ((this->x - 20) % 65 == 0) this->o = 1;
if ((this->x - 20) % 65 == 0) {
this->o = 1;
}
dir = this->o;
}
if (JI_KeyPressed(SDL_SCANCODE_RIGHT)) {
if ((this->y - 30) % 35 == 0) this->o = 2;
if ((this->y - 30) % 35 == 0) {
this->o = 2;
}
dir = this->o;
}
if (JI_KeyPressed(SDL_SCANCODE_LEFT)) {
if ((this->y - 30) % 35 == 0) this->o = 3;
if ((this->y - 30) % 35 == 0) {
this->o = 3;
}
dir = this->o;
}
switch (dir) {
case 0:
if (this->y < 170) this->y++;
if (this->y < 170) {
this->y++;
}
break;
case 1:
if (this->y > 30) this->y--;
if (this->y > 30) {
this->y--;
}
break;
case 2:
if (this->x < 280) this->x++;
if (this->x < 280) {
this->x++;
}
break;
case 3:
if (this->x > 20) this->x--;
if (this->x > 20) {
this->x--;
}
break;
}
@@ -128,13 +156,17 @@ Uint8 Prota::update() {
this->cur_frame = 0;
} else {
this->frame_pejades++;
if (this->frame_pejades == 15) this->frame_pejades = 0;
if (this->frame_pejades == 15) {
this->frame_pejades = 0;
}
if (JG_GetCycleCounter() % this->cycles_per_frame == 0) {
this->cur_frame++;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) this->cur_frame = 0;
if (this->cur_frame == entitat.animacions[this->o].frames.size()) {
this->cur_frame = 0;
}
}
}
eixir = false;
eixir = 0u;
} else {
if (JG_GetCycleCounter() % this->cycles_per_frame == 0) {
this->cur_frame++;
+1 -1
View File
@@ -8,7 +8,7 @@ class Prota : public Sprite {
explicit Prota(JD8_Surface gfx);
void draw() override;
Uint8 update();
auto update() -> Uint8;
Uint8 frame_pejades;
bool pergami;
+6 -2
View File
@@ -42,7 +42,9 @@ namespace scenes {
switch (phase_) {
case Phase::FadingIn:
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Showing;
if (fade_.done()) {
phase_ = Phase::Showing;
}
break;
case Phase::Showing:
@@ -60,7 +62,9 @@ namespace scenes {
case Phase::FadingOut:
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Done;
if (fade_.done()) {
phase_ = Phase::Done;
}
break;
case Phase::Done:
+2 -2
View File
@@ -22,8 +22,8 @@ namespace scenes {
public:
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return 0; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
[[nodiscard]] auto nextState() const -> int override { return 0; }
private:
enum class Phase { FadingIn,
+4 -2
View File
@@ -35,10 +35,12 @@ namespace scenes {
render();
}
void BootLoaderScene::render() const {
void BootLoaderScene::render() {
JD8_ClearScreen(BG_COLOR);
if (!Options::game.show_preload) return;
if (!Options::game.show_preload) {
return;
}
const float pct = Resource::Cache::get()->getProgress();
const int filled = static_cast<int>(static_cast<float>(BAR_W) * pct);
+2 -2
View File
@@ -15,10 +15,10 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return done_; }
[[nodiscard]] auto done() const -> bool override { return done_; }
private:
void render() const;
static void render();
bool done_{false};
};
+9 -9
View File
@@ -18,14 +18,14 @@ namespace {
};
constexpr CocheFrame COCHE_FRAMES[8] = {
{214, 152},
{214, 104},
{214, 56},
{214, 104},
{214, 152},
{214, 8},
{108, 152},
{214, 8},
{.x = 214, .y = 152},
{.x = 214, .y = 104},
{.x = 214, .y = 56},
{.x = 214, .y = 104},
{.x = 214, .y = 152},
{.x = 214, .y = 8},
{.x = 108, .y = 152},
{.x = 214, .y = 8},
};
constexpr int CONTADOR_MAX = 3100; // ~62 s de crèdits a 20 ms/tick
@@ -101,7 +101,7 @@ namespace scenes {
void CreditsScene::writeTrickIni() {
FILE* ini = std::fopen("trick.ini", "wb");
if (ini) {
if (ini != nullptr) {
std::fwrite("1", 1, 1, ini);
std::fclose(ini);
}
+2 -2
View File
@@ -28,7 +28,7 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
private:
enum class Phase { Rolling,
@@ -36,7 +36,7 @@ namespace scenes {
Done };
void render();
void writeTrickIni();
static void writeTrickIni();
SurfaceHandle vaddr2_; // gfx/final.gif (sprites i coches)
SurfaceHandle vaddr3_; // gfx/finals.gif (fons / parallax)
+3 -1
View File
@@ -10,7 +10,9 @@ namespace scenes {
loop_(loop) {}
void FrameAnimator::tick(int delta_ms) {
if (finished_) return;
if (finished_) {
return;
}
elapsed_ms_ += delta_ms;
while (elapsed_ms_ >= frame_ms_) {
elapsed_ms_ -= frame_ms_;
+3 -3
View File
@@ -15,9 +15,9 @@ namespace scenes {
void tick(int delta_ms);
int frame() const { return current_frame_; }
bool done() const { return !loop_ && finished_; }
int numFrames() const { return num_frames_; }
[[nodiscard]] auto frame() const -> int { return current_frame_; }
[[nodiscard]] auto done() const -> bool { return !loop_ && finished_; }
[[nodiscard]] auto numFrames() const -> int { return num_frames_; }
void reset();
void setFrameMs(int frame_ms) { frame_ms_ = frame_ms; }
+21 -7
View File
@@ -108,14 +108,28 @@ namespace scenes {
// els índexs 16..31 (grup del verd brillant del logo).
for (int i = 16; i < 32; i++) {
if (i == 17) {
if (pal_[i].r < 255) pal_[i].r++;
if (pal_[i].g < 255) pal_[i].g++;
if (pal_[i].b < 255) pal_[i].b++;
if (pal_[i].r < 255) {
pal_[i].r++;
}
if (pal_[i].g < 255) {
pal_[i].g++;
}
if (pal_[i].b < 255) {
pal_[i].b++;
}
}
if (pal_[i].b < pal_[i].g) {
pal_[i].b++;
}
if (pal_[i].b > pal_[i].g) {
pal_[i].b--;
}
if (pal_[i].r < pal_[i].g) {
pal_[i].r++;
}
if (pal_[i].r > pal_[i].g) {
pal_[i].r--;
}
if (pal_[i].b < pal_[i].g) pal_[i].b++;
if (pal_[i].b > pal_[i].g) pal_[i].b--;
if (pal_[i].r < pal_[i].g) pal_[i].r++;
if (pal_[i].r > pal_[i].g) pal_[i].r--;
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
private:
enum class Phase {
+39 -23
View File
@@ -27,21 +27,21 @@ namespace {
};
constexpr RevealStep REVEAL_STEPS[] = {
{100, 27, 68, false, false}, // J
{100, 53, 96, false, false}, // JA
{100, 66, 109, false, false}, // JAI
{200, 92, 136, false, false}, // JAIL
{200, 92, -1, true, false}, // JAIL (clear, sense avió — parpelleig)
{100, 118, 160, false, false}, // JAILG
{100, 145, 188, false, false}, // JAILGA
{100, 178, 221, false, false}, // JAILGAM
{100, 205, 248, false, false}, // JAILGAME
{200, 0, 274, false, true}, // JAILGAMES (wordmark complet) + avió
{200, 0, -1, true, true}, // JAILGAMES (clear, sense avió)
{200, 0, 274, false, true}, // JAILGAMES + avió (sense clear)
{200, 0, -1, true, true}, // JAILGAMES (clear, sense avió)
{200, 0, 274, false, true}, // JAILGAMES + avió (sense clear)
{200, 0, -1, true, true}, // JAILGAMES (clear, sense avió)
{.duration_ms = 100, .body_w = 27, .plane_x = 68, .clear = false, .wordmark = false}, // J
{.duration_ms = 100, .body_w = 53, .plane_x = 96, .clear = false, .wordmark = false}, // JA
{.duration_ms = 100, .body_w = 66, .plane_x = 109, .clear = false, .wordmark = false}, // JAI
{.duration_ms = 200, .body_w = 92, .plane_x = 136, .clear = false, .wordmark = false}, // JAIL
{.duration_ms = 200, .body_w = 92, .plane_x = -1, .clear = true, .wordmark = false}, // JAIL (clear, sense avió — parpelleig)
{.duration_ms = 100, .body_w = 118, .plane_x = 160, .clear = false, .wordmark = false}, // JAILG
{.duration_ms = 100, .body_w = 145, .plane_x = 188, .clear = false, .wordmark = false}, // JAILGA
{.duration_ms = 100, .body_w = 178, .plane_x = 221, .clear = false, .wordmark = false}, // JAILGAM
{.duration_ms = 100, .body_w = 205, .plane_x = 248, .clear = false, .wordmark = false}, // JAILGAME
{.duration_ms = 200, .body_w = 0, .plane_x = 274, .clear = false, .wordmark = true}, // JAILGAMES (wordmark complet) + avió
{.duration_ms = 200, .body_w = 0, .plane_x = -1, .clear = true, .wordmark = true}, // JAILGAMES (clear, sense avió)
{.duration_ms = 200, .body_w = 0, .plane_x = 274, .clear = false, .wordmark = true}, // JAILGAMES + avió (sense clear)
{.duration_ms = 200, .body_w = 0, .plane_x = -1, .clear = true, .wordmark = true}, // JAILGAMES (clear, sense avió)
{.duration_ms = 200, .body_w = 0, .plane_x = 274, .clear = false, .wordmark = true}, // JAILGAMES + avió (sense clear)
{.duration_ms = 200, .body_w = 0, .plane_x = -1, .clear = true, .wordmark = true}, // JAILGAMES (clear, sense avió)
};
constexpr int REVEAL_COUNT = sizeof(REVEAL_STEPS) / sizeof(REVEAL_STEPS[0]);
@@ -88,7 +88,9 @@ namespace scenes {
case Phase::Reveal: {
const RevealStep& s = REVEAL_STEPS[reveal_index_];
if (s.clear) JD8_ClearScreen(0);
if (s.clear) {
JD8_ClearScreen(0);
}
if (s.wordmark) {
drawWordmark(gfx_);
} else if (s.body_w > 0) {
@@ -121,14 +123,28 @@ namespace scenes {
// els altres convergeixen cap al mateix gris mitjà.
for (int i = 16; i < 32; i++) {
if (i == 17) {
if (pal_[i].r < 255) pal_[i].r++;
if (pal_[i].g < 255) pal_[i].g++;
if (pal_[i].b < 255) pal_[i].b++;
if (pal_[i].r < 255) {
pal_[i].r++;
}
if (pal_[i].g < 255) {
pal_[i].g++;
}
if (pal_[i].b < 255) {
pal_[i].b++;
}
}
if (pal_[i].b < pal_[i].g) {
pal_[i].b++;
}
if (pal_[i].b > pal_[i].g) {
pal_[i].b--;
}
if (pal_[i].r < pal_[i].g) {
pal_[i].r++;
}
if (pal_[i].r > pal_[i].g) {
pal_[i].r--;
}
if (pal_[i].b < pal_[i].g) pal_[i].b++;
if (pal_[i].b > pal_[i].g) pal_[i].b--;
if (pal_[i].r < pal_[i].g) pal_[i].r++;
if (pal_[i].r > pal_[i].g) pal_[i].r--;
}
}
+1 -1
View File
@@ -37,7 +37,7 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
private:
enum class Phase {
+29 -27
View File
@@ -146,17 +146,17 @@ namespace {
}
constexpr SpritePhase variant_0[] = {
{0, 200, v0_walk_right, true},
{0, 200, v0_pull_map_right, true},
{200, 0, v0_pull_map_right, true}, // guarda el mapa (reprodueix inversament)
{200, 80, v0_walk_left_to_80, true},
{0, 200, v0_pull_map_left, true},
{300, 95, v0_momia_left, true},
{0, 50, v0_turn, true},
{0, 49, v0_jump1, true},
{50, 99, v0_jump2, true},
{80, 0, v0_walk_final, true},
{0, 150, v0_final, true},
{.start_i = 0, .end_i = 200, .render = v0_walk_right, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v0_pull_map_right, .skippable = true},
{.start_i = 200, .end_i = 0, .render = v0_pull_map_right, .skippable = true}, // guarda el mapa (reprodueix inversament)
{.start_i = 200, .end_i = 80, .render = v0_walk_left_to_80, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v0_pull_map_left, .skippable = true},
{.start_i = 300, .end_i = 95, .render = v0_momia_left, .skippable = true},
{.start_i = 0, .end_i = 50, .render = v0_turn, .skippable = true},
{.start_i = 0, .end_i = 49, .render = v0_jump1, .skippable = true},
{.start_i = 50, .end_i = 99, .render = v0_jump2, .skippable = true},
{.start_i = 80, .end_i = 0, .render = v0_walk_final, .skippable = true},
{.start_i = 0, .end_i = 150, .render = v0_final, .skippable = true},
};
// =========================================================================
@@ -224,13 +224,13 @@ namespace {
}
constexpr SpritePhase variant_1[] = {
{0, 200, v1_walk_right, true},
{0, 300, v1_pull_map, true},
{0, 100, v1_interrogant, true},
{0, 200, v1_drop_map, true},
{0, 75, v1_stone_fall, true},
{0, 19, v1_stone_break, true},
{0, 200, v1_final, true},
{.start_i = 0, .end_i = 200, .render = v1_walk_right, .skippable = true},
{.start_i = 0, .end_i = 300, .render = v1_pull_map, .skippable = true},
{.start_i = 0, .end_i = 100, .render = v1_interrogant, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v1_drop_map, .skippable = true},
{.start_i = 0, .end_i = 75, .render = v1_stone_fall, .skippable = true},
{.start_i = 0, .end_i = 19, .render = v1_stone_break, .skippable = true},
{.start_i = 0, .end_i = 200, .render = v1_final, .skippable = true},
};
// =========================================================================
@@ -268,17 +268,17 @@ namespace {
}
constexpr SpritePhase variant_2[] = {
{0, 145, v2_approach, true},
{0, 100, v2_still, true},
{0, 50, v2_horn, true},
{0, 800, v2_ball, true},
{.start_i = 0, .end_i = 145, .render = v2_approach, .skippable = true},
{.start_i = 0, .end_i = 100, .render = v2_still, .skippable = true},
{.start_i = 0, .end_i = 50, .render = v2_horn, .skippable = true},
{.start_i = 0, .end_i = 800, .render = v2_ball, .skippable = true},
};
// =========================================================================
// Dispatch per variant
// =========================================================================
const SpritePhase* variant_table(int variant) {
auto variant_table(int variant) -> const SpritePhase* {
switch (variant) {
case 0:
return variant_0;
@@ -290,7 +290,7 @@ namespace {
return variant_0;
}
int variant_length(int variant) {
auto variant_length(int variant) -> int {
switch (variant) {
case 0:
return sizeof(variant_0) / sizeof(variant_0[0]);
@@ -302,11 +302,11 @@ namespace {
return 0;
}
int phase_step_count(const SpritePhase& p) {
auto phase_step_count(const SpritePhase& p) -> int {
return std::abs(p.end_i - p.start_i) + 1;
}
int phase_current_i(const SpritePhase& p, int step) {
auto phase_current_i(const SpritePhase& p, int step) -> int {
return p.end_i >= p.start_i ? p.start_i + step : p.start_i - step;
}
@@ -334,7 +334,9 @@ namespace scenes {
}
void IntroSpritesScene::tick(int delta_ms) {
if (done_) return;
if (done_) {
return;
}
const SpritePhase* phases = variant_table(variant_);
const int num_phases = variant_length(variant_);
+1 -1
View File
@@ -28,7 +28,7 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return done_; }
[[nodiscard]] auto done() const -> bool override { return done_; }
private:
SurfaceHandle gfx_;
+15 -5
View File
@@ -62,7 +62,9 @@ namespace scenes {
switch (phase_) {
case Phase::FadingIn:
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Showing;
if (fade_.done()) {
phase_ = Phase::Showing;
}
break;
case Phase::Showing: {
@@ -70,20 +72,26 @@ namespace scenes {
palmeres_acc_ms_ += delta_ms;
while (palmeres_acc_ms_ >= 80) {
palmeres_acc_ms_ -= 80;
if (--palmeres_ < 0) palmeres_ = 319;
if (--palmeres_ < 0) {
palmeres_ = 319;
}
}
// Horitzó: 1 pixel cada 320 ms (= cada 16 ticks × 20 ms)
horitzo_acc_ms_ += delta_ms;
while (horitzo_acc_ms_ >= 320) {
horitzo_acc_ms_ -= 320;
if (--horitzo_ < 0) horitzo_ = 319;
if (--horitzo_ < 0) {
horitzo_ = 319;
}
}
camello_.tick(delta_ms);
blink_ms_ += delta_ms;
if (blink_ms_ >= 2000) blink_ms_ %= 2000;
if (blink_ms_ >= 2000) {
blink_ms_ %= 2000;
}
render();
@@ -102,7 +110,9 @@ namespace scenes {
case Phase::FadingOut:
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Done;
if (fade_.done()) {
phase_ = Phase::Done;
}
break;
case Phase::Done:
+1 -1
View File
@@ -26,7 +26,7 @@ namespace scenes {
public:
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
private:
enum class Phase { FadingIn,
+6 -2
View File
@@ -32,7 +32,9 @@ namespace scenes {
switch (phase_) {
case Phase::FadingIn:
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Showing;
if (fade_.done()) {
phase_ = Phase::Showing;
}
break;
case Phase::Showing:
@@ -53,7 +55,9 @@ namespace scenes {
case Phase::FadingOut:
fade_.tick(delta_ms);
if (fade_.done()) phase_ = Phase::Done;
if (fade_.done()) {
phase_ = Phase::Done;
}
break;
case Phase::Done:
+1 -1
View File
@@ -18,7 +18,7 @@ namespace scenes {
public:
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
private:
enum class Phase { FadingIn,
+3 -1
View File
@@ -13,7 +13,9 @@ namespace scenes {
}
void PaletteFade::tick(int /*delta_ms*/) {
if (!active_) return;
if (!active_) {
return;
}
// El fade té 32 passos interns. Amb un tick per frame (~16ms)
// dura ~512ms — el mateix temps que la versió bloquejant original.
// Si en el futur volem fer-lo genuinament time-based (p.ex. "fade
+2 -2
View File
@@ -20,8 +20,8 @@ namespace scenes {
void tick(int delta_ms);
bool active() const { return active_; }
bool done() const { return !active_; }
[[nodiscard]] auto active() const -> bool { return active_; }
[[nodiscard]] auto done() const -> bool { return !active_; }
private:
bool active_{false};
+2 -2
View File
@@ -24,14 +24,14 @@ namespace scenes {
virtual void tick(int delta_ms) = 0;
virtual bool done() const = 0;
[[nodiscard]] virtual auto done() const -> bool = 0;
// Valor retornat al caller quan l'escena acaba — equivalent al int
// que retornaven les velles funcions `Go()` de ModuleSequence:
// 1 = continuar amb la següent escena segons info::ctx
// 0 = entrar al gameplay (ModuleGame)
// -1 = eixir del joc
virtual int nextState() const { return 1; }
[[nodiscard]] virtual auto nextState() const -> int { return 1; }
};
} // namespace scenes
+5 -3
View File
@@ -2,7 +2,7 @@
namespace scenes {
SceneRegistry& SceneRegistry::instance() {
auto SceneRegistry::instance() -> SceneRegistry& {
static SceneRegistry inst;
return inst;
}
@@ -11,9 +11,11 @@ namespace scenes {
factories_[state_key] = std::move(factory);
}
std::unique_ptr<Scene> SceneRegistry::tryCreate(int state_key) const {
auto SceneRegistry::tryCreate(int state_key) const -> std::unique_ptr<Scene> {
const auto it = factories_.find(state_key);
if (it == factories_.end()) return nullptr;
if (it == factories_.end()) {
return nullptr;
}
return it->second();
}
+2 -2
View File
@@ -21,13 +21,13 @@ namespace scenes {
public:
using Factory = std::function<std::unique_ptr<Scene>()>;
static SceneRegistry& instance();
static auto instance() -> SceneRegistry&;
void registerScene(int state_key, Factory factory);
// Retorna `nullptr` si no hi ha cap escena registrada per a aquest
// state. El caller hauria de caure al path legacy en aquest cas.
std::unique_ptr<Scene> tryCreate(int state_key) const;
auto tryCreate(int state_key) const -> std::unique_ptr<Scene>;
private:
SceneRegistry() = default;
+4 -2
View File
@@ -7,7 +7,7 @@
namespace scenes {
namespace {
std::string basename(const char* path) {
auto basename(const char* path) -> std::string {
std::string s = path;
auto pos = s.find_last_of("/\\");
return pos == std::string::npos ? s : s.substr(pos + 1);
@@ -15,7 +15,9 @@ namespace scenes {
} // namespace
void playMusic(const char* filename, int loop) {
if (!filename) return;
if (filename == nullptr) {
return;
}
Audio::get()->playMusic(basename(filename), loop);
}
+3 -3
View File
@@ -107,7 +107,7 @@ namespace scenes {
case Phase::Tomba1ScrollIn: {
phase_acc_ms_ += delta_ms;
const int contador = std::min(128, phase_acc_ms_ / TICK_MS + 1);
const int contador = std::min(128, (phase_acc_ms_ / TICK_MS) + 1);
// Dos blits solapats: el primer avança a velocitat completa,
// el segon (contingut de la dreta del src) a meitat (contador>>1).
JD8_Blit(70, 60, gfx_, 0, contador, 178, 70);
@@ -130,7 +130,7 @@ namespace scenes {
case Phase::Tomba2ScrollIn: {
phase_acc_ms_ += delta_ms;
const int contador = std::min(94, phase_acc_ms_ / TICK_MS + 1);
const int contador = std::min(94, (phase_acc_ms_ / TICK_MS) + 1);
JD8_Blit(55, 53, gfx_, 0, 158 - contador, 211, contador);
if (phase_acc_ms_ >= TOMBA2_SCROLL_MS) {
phase_ = Phase::Tomba2Hold;
@@ -150,7 +150,7 @@ namespace scenes {
case Phase::Tomba2Reveal: {
phase_acc_ms_ += delta_ms;
const int contador = std::min(80, phase_acc_ms_ / TICK_MS + 1);
const int contador = std::min(80, (phase_acc_ms_ / TICK_MS) + 1);
// Revelat horitzontal simètric: l'amplada creix 2px per tick
// i el src_x es desplaça a l'esquerra el mateix.
JD8_Blit(80, 68, gfx_, 160 - (contador * 2), 0, contador * 2, 64);
+3 -3
View File
@@ -31,8 +31,8 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return 0; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
[[nodiscard]] auto nextState() const -> int override { return 0; }
private:
enum class Phase {
@@ -50,7 +50,7 @@ namespace scenes {
};
void swapToTomba2();
void beginRedPulseSetup();
static void beginRedPulseSetup();
void beginFinalFade();
SurfaceHandle gfx_;
+14 -9
View File
@@ -105,7 +105,9 @@ namespace scenes {
// el final natural crida JA_FadeOutMusic (beginFinalFade() distingeix).
if (!skip_triggered_ && JI_AnyKey()) {
skip_triggered_ = true;
if (num_piramide_at_start_ != 7) Audio::get()->fadeOutMusic(250);
if (num_piramide_at_start_ != 7) {
Audio::get()->fadeOutMusic(250);
}
fade_.startFadeOut();
phase_ = Phase::FadeFinal;
}
@@ -118,7 +120,7 @@ namespace scenes {
const int slide_idx = (phase_ == Phase::Slide1Enter ? 0
: phase_ == Phase::Slide2Enter ? 1
: 2);
const float t = std::min(1.0f, static_cast<float>(phase_acc_ms_) / static_cast<float>(SCROLL_MS));
const float t = std::min(1.0F, static_cast<float>(phase_acc_ms_) / static_cast<float>(SCROLL_MS));
const float eased = Easing::outCubic(t);
const int pos_x = Easing::lerpInt(SLIDE_START_X[slide_idx], 0, eased);
drawSlide(slide_idx, pos_x);
@@ -126,12 +128,13 @@ namespace scenes {
if (phase_acc_ms_ >= SCROLL_MS) {
// Garanteix posició final exacta (pos_x=0).
drawSlide(slide_idx, 0);
if (phase_ == Phase::Slide1Enter)
if (phase_ == Phase::Slide1Enter) {
phase_ = Phase::Slide1Hold;
else if (phase_ == Phase::Slide2Enter)
} else if (phase_ == Phase::Slide2Enter) {
phase_ = Phase::Slide2Hold;
else
} else {
phase_ = Phase::Slide3Hold;
}
phase_acc_ms_ = 0;
}
break;
@@ -142,10 +145,11 @@ namespace scenes {
phase_acc_ms_ += delta_ms;
if (phase_acc_ms_ >= HOLD_MS) {
fade_.startFadeOut();
if (phase_ == Phase::Slide1Hold)
if (phase_ == Phase::Slide1Hold) {
phase_ = Phase::FadeOut1;
else
} else {
phase_ = Phase::FadeOut2;
}
phase_acc_ms_ = 0;
}
break;
@@ -163,10 +167,11 @@ namespace scenes {
if (fade_.done()) {
restorePalette();
JD8_ClearScreen(BG_COLOR_INDEX);
if (phase_ == Phase::FadeOut1)
if (phase_ == Phase::FadeOut1) {
phase_ = Phase::Slide2Enter;
else
} else {
phase_ = Phase::Slide3Enter;
}
phase_acc_ms_ = 0;
}
break;
+2 -2
View File
@@ -40,8 +40,8 @@ namespace scenes {
void onEnter() override;
void tick(int delta_ms) override;
bool done() const override { return phase_ == Phase::Done; }
int nextState() const override { return next_state_; }
[[nodiscard]] auto done() const -> bool override { return phase_ == Phase::Done; }
[[nodiscard]] auto nextState() const -> int override { return next_state_; }
private:
enum class Phase {
+5 -3
View File
@@ -11,7 +11,7 @@ namespace scenes {
y1_ = y1;
duration_ms_ = std::max(0, duration_ms);
elapsed_ms_ = 0;
ease_ = ease ? ease : Easing::linear;
ease_ = (ease != nullptr) ? ease : Easing::linear;
cur_x_ = x0;
cur_y_ = y0;
}
@@ -38,8 +38,10 @@ namespace scenes {
cur_y_ = Easing::lerpInt(y0_, y1_, eased);
}
float SpriteMover::progress() const {
if (duration_ms_ <= 0) return 1.0f;
auto SpriteMover::progress() const -> float {
if (duration_ms_ <= 0) {
return 1.0F;
}
return static_cast<float>(elapsed_ms_) / static_cast<float>(duration_ms_);
}
+4 -4
View File
@@ -22,10 +22,10 @@ namespace scenes {
void tick(int delta_ms);
int x() const { return cur_x_; }
int y() const { return cur_y_; }
bool done() const { return elapsed_ms_ >= duration_ms_; }
float progress() const; // 0..1 sense easing aplicat
[[nodiscard]] auto x() const -> int { return cur_x_; }
[[nodiscard]] auto y() const -> int { return cur_y_; }
[[nodiscard]] auto done() const -> bool { return elapsed_ms_ >= duration_ms_; }
[[nodiscard]] auto progress() const -> float; // 0..1 sense easing aplicat
private:
int x0_{0}, y0_{0}, x1_{0}, y1_{0};
+15 -7
View File
@@ -6,7 +6,9 @@ namespace scenes {
: surface_(JD8_LoadSurface(file)) {}
SurfaceHandle::~SurfaceHandle() {
if (surface_) JD8_FreeSurface(surface_);
if (surface_ != nullptr) {
JD8_FreeSurface(surface_);
}
}
SurfaceHandle::SurfaceHandle(SurfaceHandle&& other) noexcept
@@ -14,9 +16,11 @@ namespace scenes {
other.surface_ = nullptr;
}
SurfaceHandle& SurfaceHandle::operator=(SurfaceHandle&& other) noexcept {
auto SurfaceHandle::operator=(SurfaceHandle&& other) noexcept -> SurfaceHandle& {
if (this != &other) {
if (surface_) JD8_FreeSurface(surface_);
if (surface_ != nullptr) {
JD8_FreeSurface(surface_);
}
surface_ = other.surface_;
other.surface_ = nullptr;
}
@@ -24,16 +28,20 @@ namespace scenes {
}
void SurfaceHandle::reset(const char* file) {
if (surface_) JD8_FreeSurface(surface_);
surface_ = file ? JD8_LoadSurface(file) : nullptr;
if (surface_ != nullptr) {
JD8_FreeSurface(surface_);
}
surface_ = (file != nullptr) ? JD8_LoadSurface(file) : nullptr;
}
void SurfaceHandle::adopt(JD8_Surface raw) {
if (surface_) JD8_FreeSurface(surface_);
if (surface_ != nullptr) {
JD8_FreeSurface(surface_);
}
surface_ = raw;
}
JD8_Surface SurfaceHandle::release() {
auto SurfaceHandle::release() -> JD8_Surface {
JD8_Surface r = surface_;
surface_ = nullptr;
return r;
+5 -5
View File
@@ -15,10 +15,10 @@ namespace scenes {
~SurfaceHandle();
SurfaceHandle(const SurfaceHandle&) = delete;
SurfaceHandle& operator=(const SurfaceHandle&) = delete;
auto operator=(const SurfaceHandle&) -> SurfaceHandle& = delete;
SurfaceHandle(SurfaceHandle&& other) noexcept;
SurfaceHandle& operator=(SurfaceHandle&& other) noexcept;
auto operator=(SurfaceHandle&& other) noexcept -> SurfaceHandle&;
// Allibera la surface actual (si n'hi ha) i carrega una nova.
// Usat per escenes que recarreguen assets a mitja cinemàtica
@@ -34,13 +34,13 @@ namespace scenes {
// altre propietari). Usat quan una escena delega a codi legacy que
// també allibera la mateixa surface — cal "soltar" el ownership per
// evitar double free.
[[nodiscard]] JD8_Surface release();
[[nodiscard]] auto release() -> JD8_Surface;
// Conversió implícita per al confort d'ús: JD8_Blit(handle)
// en lloc de JD8_Blit(handle.get()).
operator JD8_Surface() const { return surface_; }
JD8_Surface get() const { return surface_; }
bool valid() const { return surface_ != nullptr; }
[[nodiscard]] auto get() const -> JD8_Surface { return surface_; }
[[nodiscard]] auto valid() const -> bool { return surface_ != nullptr; }
private:
JD8_Surface surface_{nullptr};
+28 -12
View File
@@ -4,7 +4,7 @@
namespace scenes {
Timeline& Timeline::step(int duration_ms, StepFn fn) {
auto Timeline::step(int duration_ms, StepFn fn) -> Timeline& {
Step s;
s.duration_ms = duration_ms;
s.continuous = std::move(fn);
@@ -12,7 +12,7 @@ namespace scenes {
return *this;
}
Timeline& Timeline::once(OnceFn fn) {
auto Timeline::once(OnceFn fn) -> Timeline& {
Step s;
s.duration_ms = 0;
s.oneshot = std::move(fn);
@@ -25,7 +25,9 @@ namespace scenes {
auto& s = steps_[current_];
if (!s.entered) {
s.entered = true;
if (s.oneshot) s.oneshot();
if (s.oneshot) {
s.oneshot();
}
}
++current_;
elapsed_in_step_ = 0;
@@ -33,21 +35,29 @@ namespace scenes {
}
void Timeline::tick(int delta_ms) {
if (skipped_) return;
if (skipped_) {
return;
}
flushOneShots();
if (current_ >= steps_.size()) return;
if (current_ >= steps_.size()) {
return;
}
auto& s = steps_[current_];
if (!s.entered) {
s.entered = true;
// Primer tick dins del pas: cridem amb progress=0 si hi ha callback.
if (s.continuous) s.continuous(0.0f);
if (s.continuous) {
s.continuous(0.0F);
}
}
elapsed_in_step_ += delta_ms;
if (elapsed_in_step_ >= s.duration_ms) {
// Tancament del pas: una crida final amb progress=1.
if (s.continuous) s.continuous(1.0f);
if (s.continuous) {
s.continuous(1.0F);
}
++current_;
elapsed_in_step_ = 0;
// Pot ser que el següent pas siga una cadena de one-shots.
@@ -65,20 +75,26 @@ namespace scenes {
}
void Timeline::reset() {
for (auto& s : steps_) s.entered = false;
for (auto& s : steps_) {
s.entered = false;
}
current_ = 0;
elapsed_in_step_ = 0;
skipped_ = false;
}
bool Timeline::done() const {
auto Timeline::done() const -> bool {
return skipped_ || current_ >= steps_.size();
}
float Timeline::currentProgress() const {
if (current_ >= steps_.size()) return 1.0f;
auto Timeline::currentProgress() const -> float {
if (current_ >= steps_.size()) {
return 1.0F;
}
const auto& s = steps_[current_];
if (s.duration_ms <= 0) return 0.0f;
if (s.duration_ms <= 0) {
return 0.0F;
}
return static_cast<float>(elapsed_in_step_) / static_cast<float>(s.duration_ms);
}
+5 -5
View File
@@ -25,16 +25,16 @@ namespace scenes {
using StepFn = std::function<void(float progress_0_1)>;
using OnceFn = std::function<void()>;
Timeline& step(int duration_ms, StepFn fn = nullptr);
Timeline& once(OnceFn fn);
auto step(int duration_ms, StepFn fn = nullptr) -> Timeline&;
auto once(OnceFn fn) -> Timeline&;
void tick(int delta_ms);
void skip();
void reset();
bool done() const;
int currentStepIndex() const { return static_cast<int>(current_); }
float currentProgress() const;
[[nodiscard]] auto done() const -> bool;
[[nodiscard]] auto currentStepIndex() const -> int { return static_cast<int>(current_); }
[[nodiscard]] auto currentProgress() const -> float;
private:
struct Step {
+7 -5
View File
@@ -26,7 +26,7 @@
#include "core/system/director.hpp"
#include "game/options.hpp"
SDL_AppResult SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) {
auto SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) -> SDL_AppResult {
srand(unsigned(time(nullptr)));
// Crea la carpeta de configuració i carrega les opcions
@@ -37,7 +37,7 @@ SDL_AppResult SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) {
// (retorna Contents/Resources/) o en un executable normal (carpeta del binari).
const char* base_path = SDL_GetBasePath();
std::string resource_pack_path;
if (base_path) {
if (base_path != nullptr) {
const std::string data_path = std::string(base_path) + "data/";
file_setresourcefolder(data_path.c_str());
resource_pack_path = std::string(base_path) + "resources.pack";
@@ -109,7 +109,7 @@ SDL_AppResult SDL_AppInit(void** /*appstate*/, int /*argc*/, char* /*argv*/[]) {
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void* /*appstate*/) {
auto SDL_AppIterate(void* /*appstate*/) -> SDL_AppResult {
// Una iteració del bucle del Director. Abans els events es drenaven
// amb SDL_PollEvent dins d'aquesta funció; ara SDL ens els lliura
// d'un en un via SDL_AppEvent, així que iterate() no els toca.
@@ -119,8 +119,10 @@ SDL_AppResult SDL_AppIterate(void* /*appstate*/) {
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void* /*appstate*/, SDL_Event* event) {
if (!event) return SDL_APP_CONTINUE;
auto SDL_AppEvent(void* /*appstate*/, SDL_Event* event) -> SDL_AppResult {
if (event == nullptr) {
return SDL_APP_CONTINUE;
}
Director::get()->handleEvent(*event);
if (Director::get()->isQuitRequested()) {
return SDL_APP_SUCCESS;
+4 -4
View File
@@ -4,19 +4,19 @@ namespace Easing {
auto linear(float t) -> float { return t; }
auto outQuad(float t) -> float { return 1.0F - (1.0F - t) * (1.0F - t); }
auto outQuad(float t) -> float { return 1.0F - ((1.0F - t) * (1.0F - t)); }
auto inQuad(float t) -> float { return t * t; }
auto inOutQuad(float t) -> float {
return t < 0.5F ? 2.0F * t * t : 1.0F - (-2.0F * t + 2.0F) * (-2.0F * t + 2.0F) / 2.0F;
return t < 0.5F ? 2.0F * t * t : 1.0F - ((-2.0F * t + 2.0F) * (-2.0F * t + 2.0F) / 2.0F);
}
auto outCubic(float t) -> float {
const float inv = 1.0F - t;
return 1.0F - inv * inv * inv;
return 1.0F - (inv * inv * inv);
}
auto inCubic(float t) -> float { return t * t * t; }
auto lerp(float a, float b, float t) -> float { return a + (b - a) * t; }
auto lerp(float a, float b, float t) -> float { return a + ((b - a) * t); }
auto lerpInt(int a, int b, float t) -> int {
return a + static_cast<int>((b - a) * t);
}