neteja tidy a source/core i encamina Texture::loadFromFile pel ResourceHelper
This commit is contained in:
@@ -51,7 +51,7 @@ void Audio::update() {
|
||||
JA_Update();
|
||||
|
||||
// Sincronizar estado: detectar cuando la música se para (ej. fade-out completado)
|
||||
if (instance && instance->music_.state == MusicState::PLAYING && JA_GetMusicState() != JA_MUSIC_PLAYING) {
|
||||
if ((instance != nullptr) && instance->music_.state == MusicState::PLAYING && JA_GetMusicState() != JA_MUSIC_PLAYING) {
|
||||
instance->music_.state = MusicState::STOPPED;
|
||||
}
|
||||
}
|
||||
@@ -65,10 +65,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);
|
||||
@@ -86,7 +90,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);
|
||||
|
||||
@@ -60,7 +60,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;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
#include "core/resources/resource.h"
|
||||
|
||||
namespace AudioResource {
|
||||
JA_Music_t* getMusic(const std::string& name) {
|
||||
auto getMusic(const std::string& name) -> JA_Music_t* {
|
||||
return Resource::get()->getMusic(name);
|
||||
}
|
||||
|
||||
JA_Sound_t* getSound(const std::string& name) {
|
||||
auto getSound(const std::string& name) -> JA_Sound_t* {
|
||||
return Resource::get()->getSound(name);
|
||||
}
|
||||
} // namespace AudioResource
|
||||
|
||||
@@ -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
|
||||
|
||||
+218
-117
@@ -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),
|
||||
&err,
|
||||
nullptr);
|
||||
if (!music->vorbis) {
|
||||
if (music->vorbis == nullptr) {
|
||||
std::cout << "JA_LoadMusic: stb_vorbis_open_memory failed (error " << err << ")" << '\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(¤t_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(¤t_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;
|
||||
|
||||
+38
-49
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <algorithm> // for any_of
|
||||
#include <iostream> // for basic_ostream, operator<<, cout, basi...
|
||||
#include <utility>
|
||||
|
||||
// Emscripten-only: SDL 3.4+ ja no casa el GUID dels mandos de Chrome Android
|
||||
// amb gamecontrollerdb (el gamepad.id d'Android no porta Vendor/Product, el
|
||||
@@ -58,12 +59,8 @@ auto Input::get() -> Input * {
|
||||
}
|
||||
|
||||
// Constructor
|
||||
Input::Input(const std::string &file)
|
||||
: numGamepads(0),
|
||||
dbPath(file),
|
||||
verbose(true),
|
||||
disabledUntil(d_notDisabled),
|
||||
enabled(true) {
|
||||
Input::Input(std::string file)
|
||||
: dbPath(std::move(file)) {
|
||||
// Inicializa las variables
|
||||
keyBindings_t kb;
|
||||
kb.scancode = 0;
|
||||
@@ -107,7 +104,7 @@ void Input::bindGameControllerButton(Uint8 input, SDL_GamepadButton button) {
|
||||
}
|
||||
|
||||
// Comprueba si un input esta activo
|
||||
bool Input::checkInput(Uint8 input, bool repeat, int device, int index) {
|
||||
auto Input::checkInput(Uint8 input, bool repeat, int device, int index) -> bool {
|
||||
if (!enabled) {
|
||||
return false;
|
||||
}
|
||||
@@ -123,11 +120,7 @@ bool Input::checkInput(Uint8 input, bool repeat, int device, int index) {
|
||||
const bool *keyStates = SDL_GetKeyboardState(nullptr);
|
||||
|
||||
if (repeat) {
|
||||
if (keyStates[keyBindings[input].scancode]) {
|
||||
successKeyboard = true;
|
||||
} else {
|
||||
successKeyboard = false;
|
||||
}
|
||||
successKeyboard = keyStates[keyBindings[input].scancode];
|
||||
} else {
|
||||
if (!keyBindings[input].active) {
|
||||
if (keyStates[keyBindings[input].scancode]) {
|
||||
@@ -147,14 +140,10 @@ bool Input::checkInput(Uint8 input, bool repeat, int device, int index) {
|
||||
}
|
||||
}
|
||||
|
||||
if (gameControllerFound() && index >= 0 && index < (int)connectedControllers.size())
|
||||
if (gameControllerFound() && index >= 0 && index < (int)connectedControllers.size()) {
|
||||
if ((device == INPUT_USE_GAMECONTROLLER) || (device == INPUT_USE_ANY)) {
|
||||
if (repeat) {
|
||||
if (SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[input].button)) {
|
||||
successGameController = true;
|
||||
} else {
|
||||
successGameController = false;
|
||||
}
|
||||
successGameController = SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[input].button);
|
||||
} else {
|
||||
if (!gameControllerBindings[input].active) {
|
||||
if (SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[input].button)) {
|
||||
@@ -173,12 +162,13 @@ bool Input::checkInput(Uint8 input, bool repeat, int device, int index) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (successKeyboard || successGameController);
|
||||
}
|
||||
|
||||
// Comprueba si hay almenos un input activo
|
||||
bool Input::checkAnyInput(int device, int index) {
|
||||
auto Input::checkAnyInput(int device, int index) -> bool {
|
||||
if (device == INPUT_USE_ANY) {
|
||||
index = 0;
|
||||
}
|
||||
@@ -186,8 +176,8 @@ bool Input::checkAnyInput(int device, int index) {
|
||||
if (device == INPUT_USE_KEYBOARD || device == INPUT_USE_ANY) {
|
||||
const bool *mKeystates = SDL_GetKeyboardState(nullptr);
|
||||
|
||||
for (int i = 0; i < (int)keyBindings.size(); ++i) {
|
||||
if (mKeystates[keyBindings[i].scancode]) {
|
||||
for (auto &keyBinding : keyBindings) {
|
||||
if (mKeystates[keyBinding.scancode]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -195,8 +185,8 @@ bool Input::checkAnyInput(int device, int index) {
|
||||
|
||||
if (gameControllerFound() && index >= 0 && index < (int)connectedControllers.size()) {
|
||||
if (device == INPUT_USE_GAMECONTROLLER || device == INPUT_USE_ANY) {
|
||||
for (int i = 0; i < (int)gameControllerBindings.size(); ++i) {
|
||||
if (SDL_GetGamepadButton(connectedControllers[index], gameControllerBindings[i].button)) {
|
||||
for (auto &gameControllerBinding : gameControllerBindings) {
|
||||
if (SDL_GetGamepadButton(connectedControllers[index], gameControllerBinding.button)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -209,10 +199,10 @@ bool Input::checkAnyInput(int device, int index) {
|
||||
// Construye el nombre visible de un mando.
|
||||
// Recorta des del primer '(' o '[' (per a evitar coses tipus
|
||||
// "Retroid Controller (vendor: 1001) ...") i talla a 25 caràcters.
|
||||
std::string Input::buildControllerName(SDL_Gamepad *pad, int padIndex) {
|
||||
auto Input::buildControllerName(SDL_Gamepad *pad, int padIndex) -> std::string {
|
||||
(void)padIndex;
|
||||
const char *padName = SDL_GetGamepadName(pad);
|
||||
std::string name = padName ? padName : "Unknown";
|
||||
std::string name = (padName != nullptr) ? padName : "Unknown";
|
||||
const auto pos = name.find_first_of("([");
|
||||
if (pos != std::string::npos) {
|
||||
name.erase(pos);
|
||||
@@ -228,7 +218,7 @@ std::string Input::buildControllerName(SDL_Gamepad *pad, int padIndex) {
|
||||
|
||||
// Busca si hay un mando conectado. Cierra y limpia el estado previo para
|
||||
// que la función sea idempotente si se invoca más de una vez.
|
||||
bool Input::discoverGameController() {
|
||||
auto Input::discoverGameController() -> bool {
|
||||
// Cierra los mandos ya abiertos y limpia los vectores paralelos
|
||||
for (auto *pad : connectedControllers) {
|
||||
if (pad != nullptr) {
|
||||
@@ -248,14 +238,14 @@ bool Input::discoverGameController() {
|
||||
|
||||
if (SDL_AddGamepadMappingsFromFile(dbPath.c_str()) < 0) {
|
||||
if (verbose) {
|
||||
std::cout << "Error, could not load " << dbPath.c_str() << " file: " << SDL_GetError() << std::endl;
|
||||
std::cout << "Error, could not load " << dbPath.c_str() << " file: " << SDL_GetError() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int nJoysticks = 0;
|
||||
SDL_JoystickID *joysticks = SDL_GetJoysticks(&nJoysticks);
|
||||
|
||||
if (joysticks) {
|
||||
if (joysticks != nullptr) {
|
||||
int gamepadCount = 0;
|
||||
for (int i = 0; i < nJoysticks; ++i) {
|
||||
if (SDL_IsGamepad(joysticks[i])) {
|
||||
@@ -273,7 +263,9 @@ bool Input::discoverGameController() {
|
||||
int padIndex = 0;
|
||||
|
||||
for (int i = 0; i < nJoysticks; i++) {
|
||||
if (!SDL_IsGamepad(joysticks[i])) continue;
|
||||
if (!SDL_IsGamepad(joysticks[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
installWebStandardMapping(joysticks[i]);
|
||||
SDL_Gamepad *pad = SDL_OpenGamepad(joysticks[i]);
|
||||
@@ -285,11 +277,11 @@ bool Input::discoverGameController() {
|
||||
numGamepads++;
|
||||
padIndex++;
|
||||
if (verbose) {
|
||||
std::cout << name << std::endl;
|
||||
std::cout << name << '\n';
|
||||
}
|
||||
} else {
|
||||
if (verbose) {
|
||||
std::cout << "SDL_GetError() = " << SDL_GetError() << std::endl;
|
||||
std::cout << "SDL_GetError() = " << SDL_GetError() << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,13 +296,13 @@ bool Input::discoverGameController() {
|
||||
}
|
||||
|
||||
// Procesa un evento SDL_EVENT_GAMEPAD_ADDED
|
||||
bool Input::handleGamepadAdded(SDL_JoystickID jid, std::string &outName) {
|
||||
auto Input::handleGamepadAdded(SDL_JoystickID jid, std::string &outName) -> bool {
|
||||
if (!SDL_IsGamepad(jid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si el mando ya está registrado no hace nada (ej. evento retroactivo tras el scan inicial)
|
||||
if (std::any_of(connectedControllerIds.begin(), connectedControllerIds.end(), [jid](SDL_JoystickID existing) { return existing == jid; })) {
|
||||
if (std::ranges::any_of(connectedControllerIds, [jid](SDL_JoystickID existing) { return existing == jid; })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -318,7 +310,7 @@ bool Input::handleGamepadAdded(SDL_JoystickID jid, std::string &outName) {
|
||||
SDL_Gamepad *pad = SDL_OpenGamepad(jid);
|
||||
if (pad == nullptr) {
|
||||
if (verbose) {
|
||||
std::cout << "Failed to open gamepad " << jid << ": " << SDL_GetError() << std::endl;
|
||||
std::cout << "Failed to open gamepad " << jid << ": " << SDL_GetError() << '\n';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -331,7 +323,7 @@ bool Input::handleGamepadAdded(SDL_JoystickID jid, std::string &outName) {
|
||||
numGamepads++;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Gamepad connected: " << name << std::endl;
|
||||
std::cout << "Gamepad connected: " << name << '\n';
|
||||
}
|
||||
|
||||
outName = name;
|
||||
@@ -339,9 +331,11 @@ bool Input::handleGamepadAdded(SDL_JoystickID jid, std::string &outName) {
|
||||
}
|
||||
|
||||
// Procesa un evento SDL_EVENT_GAMEPAD_REMOVED
|
||||
bool Input::handleGamepadRemoved(SDL_JoystickID jid, std::string &outName) {
|
||||
auto Input::handleGamepadRemoved(SDL_JoystickID jid, std::string &outName) -> bool {
|
||||
for (size_t i = 0; i < connectedControllerIds.size(); ++i) {
|
||||
if (connectedControllerIds[i] != jid) continue;
|
||||
if (connectedControllerIds[i] != jid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outName = controllerNames[i];
|
||||
if (connectedControllers[i] != nullptr) {
|
||||
@@ -351,10 +345,10 @@ bool Input::handleGamepadRemoved(SDL_JoystickID jid, std::string &outName) {
|
||||
connectedControllerIds.erase(connectedControllerIds.begin() + i);
|
||||
controllerNames.erase(controllerNames.begin() + i);
|
||||
numGamepads--;
|
||||
if (numGamepads < 0) numGamepads = 0;
|
||||
numGamepads = std::max(numGamepads, 0);
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Gamepad disconnected: " << outName << std::endl;
|
||||
std::cout << "Gamepad disconnected: " << outName << '\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -363,25 +357,20 @@ bool Input::handleGamepadRemoved(SDL_JoystickID jid, std::string &outName) {
|
||||
}
|
||||
|
||||
// Comprueba si hay algun mando conectado
|
||||
bool Input::gameControllerFound() {
|
||||
if (numGamepads > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
auto Input::gameControllerFound() const -> bool {
|
||||
return numGamepads > 0;
|
||||
}
|
||||
|
||||
// Obten el nombre de un mando de juego
|
||||
std::string Input::getControllerName(int index) {
|
||||
auto Input::getControllerName(int index) -> std::string {
|
||||
if (numGamepads > 0) {
|
||||
return controllerNames[index];
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Obten el numero de mandos conectados
|
||||
int Input::getNumControllers() {
|
||||
auto Input::getNumControllers() const -> int {
|
||||
return numGamepads;
|
||||
}
|
||||
|
||||
|
||||
+19
-18
@@ -2,8 +2,9 @@
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <string> // for string, basic_string
|
||||
#include <vector> // for vector
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <string> // for string, basic_string
|
||||
#include <vector> // for vector
|
||||
|
||||
// Valores de repetición
|
||||
constexpr bool REPEAT_TRUE = true;
|
||||
@@ -14,7 +15,7 @@ constexpr int INPUT_USE_KEYBOARD = 0;
|
||||
constexpr int INPUT_USE_GAMECONTROLLER = 1;
|
||||
constexpr int INPUT_USE_ANY = 2;
|
||||
|
||||
enum inputs_e {
|
||||
enum inputs_e : std::uint8_t {
|
||||
// Inputs obligatorios
|
||||
input_null,
|
||||
input_up,
|
||||
@@ -43,7 +44,7 @@ enum inputs_e {
|
||||
input_number_of_inputs
|
||||
};
|
||||
|
||||
enum i_disable_e {
|
||||
enum i_disable_e : std::uint8_t {
|
||||
d_notDisabled,
|
||||
d_forever,
|
||||
d_keyPressed
|
||||
@@ -69,17 +70,17 @@ class Input {
|
||||
std::vector<keyBindings_t> keyBindings; // Vector con las teclas asociadas a los inputs predefinidos
|
||||
std::vector<GameControllerBindings_t> gameControllerBindings; // Vector con las teclas asociadas a los inputs predefinidos
|
||||
std::vector<std::string> controllerNames; // Vector con los nombres de los mandos
|
||||
int numGamepads; // Numero de mandos conectados
|
||||
int numGamepads{0}; // Numero de mandos conectados
|
||||
std::string dbPath; // Ruta al archivo gamecontrollerdb.txt
|
||||
bool verbose; // Indica si ha de mostrar mensajes
|
||||
i_disable_e disabledUntil; // Tiempo que esta deshabilitado
|
||||
bool enabled; // Indica si está habilitado
|
||||
bool verbose{true}; // Indica si ha de mostrar mensajes
|
||||
i_disable_e disabledUntil{d_notDisabled}; // Tiempo que esta deshabilitado
|
||||
bool enabled{true}; // Indica si está habilitado
|
||||
|
||||
// Construye el nombre visible de un mando (name truncado + sufijo #N)
|
||||
std::string buildControllerName(SDL_Gamepad *pad, int padIndex);
|
||||
static auto buildControllerName(SDL_Gamepad *pad, int padIndex) -> std::string;
|
||||
|
||||
// Constructor privado (usar Input::init)
|
||||
explicit Input(const std::string &file);
|
||||
explicit Input(std::string file);
|
||||
|
||||
// Instancia única
|
||||
static Input *instance;
|
||||
@@ -103,30 +104,30 @@ class Input {
|
||||
void bindGameControllerButton(Uint8 input, SDL_GamepadButton button);
|
||||
|
||||
// Comprueba si un input esta activo
|
||||
bool checkInput(Uint8 input, bool repeat = true, int device = INPUT_USE_ANY, int index = 0);
|
||||
auto checkInput(Uint8 input, bool repeat = true, int device = INPUT_USE_ANY, int index = 0) -> bool;
|
||||
|
||||
// Comprueba si hay almenos un input activo
|
||||
bool checkAnyInput(int device = INPUT_USE_ANY, int index = 0);
|
||||
auto checkAnyInput(int device = INPUT_USE_ANY, int index = 0) -> bool;
|
||||
|
||||
// Busca si hay un mando conectado
|
||||
bool discoverGameController();
|
||||
auto discoverGameController() -> bool;
|
||||
|
||||
// Procesa un evento SDL_EVENT_GAMEPAD_ADDED. Devuelve true si el mando se ha añadido
|
||||
// (no estaba ya registrado) y escribe el nombre visible en outName.
|
||||
bool handleGamepadAdded(SDL_JoystickID jid, std::string &outName);
|
||||
auto handleGamepadAdded(SDL_JoystickID jid, std::string &outName) -> bool;
|
||||
|
||||
// Procesa un evento SDL_EVENT_GAMEPAD_REMOVED. Devuelve true si se ha encontrado y
|
||||
// eliminado, y escribe el nombre visible en outName.
|
||||
bool handleGamepadRemoved(SDL_JoystickID jid, std::string &outName);
|
||||
auto handleGamepadRemoved(SDL_JoystickID jid, std::string &outName) -> bool;
|
||||
|
||||
// Comprueba si hay algun mando conectado
|
||||
bool gameControllerFound();
|
||||
[[nodiscard]] auto gameControllerFound() const -> bool;
|
||||
|
||||
// Obten el numero de mandos conectados
|
||||
int getNumControllers();
|
||||
[[nodiscard]] auto getNumControllers() const -> int;
|
||||
|
||||
// Obten el nombre de un mando de juego
|
||||
std::string getControllerName(int index);
|
||||
auto getControllerName(int index) -> std::string;
|
||||
|
||||
// Establece si ha de mostrar mensajes
|
||||
void setVerbose(bool value);
|
||||
|
||||
@@ -24,15 +24,13 @@ auto Lang::get() -> Lang * {
|
||||
}
|
||||
|
||||
// Constructor
|
||||
Lang::Lang() {
|
||||
}
|
||||
Lang::Lang() = default;
|
||||
|
||||
// Destructor
|
||||
Lang::~Lang() {
|
||||
}
|
||||
Lang::~Lang() = default;
|
||||
|
||||
// Inicializa los textos del juego en el idioma seleccionado
|
||||
bool Lang::setLang(Uint8 lang) {
|
||||
auto Lang::setLang(Uint8 lang) -> bool {
|
||||
std::string file;
|
||||
|
||||
switch (lang) {
|
||||
@@ -53,8 +51,9 @@ bool Lang::setLang(Uint8 lang) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_TEXT_STRINGS; i++)
|
||||
mTextStrings[i] = "";
|
||||
for (auto &mTextString : mTextStrings) {
|
||||
mTextString = "";
|
||||
}
|
||||
|
||||
// Lee el fichero via ResourceHelper (pack o filesystem)
|
||||
auto bytes = ResourceHelper::loadFile(file);
|
||||
@@ -90,6 +89,6 @@ bool Lang::setLang(Uint8 lang) {
|
||||
}
|
||||
|
||||
// Obtiene la cadena de texto del indice
|
||||
std::string Lang::getText(int index) {
|
||||
auto Lang::getText(int index) -> std::string {
|
||||
return mTextStrings[index];
|
||||
}
|
||||
@@ -34,8 +34,8 @@ class Lang {
|
||||
~Lang();
|
||||
|
||||
// Inicializa los textos del juego en el idioma seleccionado
|
||||
bool setLang(Uint8 lang);
|
||||
auto setLang(Uint8 lang) -> bool;
|
||||
|
||||
// Obtiene la cadena de texto del indice
|
||||
std::string getText(int index);
|
||||
auto getText(int index) -> std::string;
|
||||
};
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "core/rendering/texture.h" // for Texture
|
||||
|
||||
// Parser compartido: lee un istream con el formato .ani
|
||||
static animatedSprite_t parseAnimationStream(std::istream &file, Texture *texture, const std::string &filename, bool verbose) {
|
||||
static auto parseAnimationStream(std::istream &file, Texture *texture, const std::string &filename, bool verbose) -> animatedSprite_t {
|
||||
animatedSprite_t as;
|
||||
as.texture = texture;
|
||||
int framesPerRow = 0;
|
||||
@@ -17,13 +17,15 @@ static animatedSprite_t parseAnimationStream(std::istream &file, Texture *textur
|
||||
std::string line;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Animation loaded: " << filename << std::endl;
|
||||
std::cout << "Animation loaded: " << filename << '\n';
|
||||
}
|
||||
// Normalitza CRLF: fitxers .ani amb terminadors de Windows fan que
|
||||
// line == "[animation]" no faci match i el parser entri en bucle
|
||||
// infinit / no carregui cap animació.
|
||||
auto strip_cr = [](std::string &s) {
|
||||
if (!s.empty() && s.back() == '\r') s.pop_back();
|
||||
if (!s.empty() && s.back() == '\r') {
|
||||
s.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
@@ -37,10 +39,12 @@ static animatedSprite_t parseAnimationStream(std::istream &file, Texture *textur
|
||||
buffer.completed = false;
|
||||
|
||||
do {
|
||||
if (!std::getline(file, line)) break;
|
||||
if (!std::getline(file, line)) {
|
||||
break;
|
||||
}
|
||||
strip_cr(line);
|
||||
int pos = line.find("=");
|
||||
if (pos != (int)line.npos) {
|
||||
int pos = line.find('=');
|
||||
if (pos != (int)std::string::npos) {
|
||||
if (line.substr(0, pos) == "name") {
|
||||
buffer.name = line.substr(pos + 1, line.length());
|
||||
} else if (line.substr(0, pos) == "speed") {
|
||||
@@ -58,15 +62,15 @@ static animatedSprite_t parseAnimationStream(std::istream &file, Texture *textur
|
||||
buffer.frames.push_back(rect);
|
||||
}
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << '\n';
|
||||
}
|
||||
}
|
||||
} while (line != "[/animation]");
|
||||
|
||||
as.animations.push_back(buffer);
|
||||
} else {
|
||||
int pos = line.find("=");
|
||||
if (pos != (int)line.npos) {
|
||||
int pos = line.find('=');
|
||||
if (pos != (int)std::string::npos) {
|
||||
if (line.substr(0, pos) == "framesPerRow") {
|
||||
framesPerRow = std::stoi(line.substr(pos + 1, line.length()));
|
||||
} else if (line.substr(0, pos) == "frameWidth") {
|
||||
@@ -74,7 +78,7 @@ static animatedSprite_t parseAnimationStream(std::istream &file, Texture *textur
|
||||
} else if (line.substr(0, pos) == "frameHeight") {
|
||||
frameHeight = std::stoi(line.substr(pos + 1, line.length()));
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << std::endl;
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << line.substr(0, pos).c_str() << "\"" << '\n';
|
||||
}
|
||||
|
||||
if (framesPerRow == 0 && frameWidth > 0) {
|
||||
@@ -93,12 +97,12 @@ static animatedSprite_t parseAnimationStream(std::istream &file, Texture *textur
|
||||
}
|
||||
|
||||
// Carga la animación desde un fichero
|
||||
animatedSprite_t loadAnimationFromFile(Texture *texture, const std::string &filePath, bool verbose) {
|
||||
auto loadAnimationFromFile(Texture *texture, const std::string &filePath, bool verbose) -> animatedSprite_t {
|
||||
const std::string filename = filePath.substr(filePath.find_last_of("\\/") + 1);
|
||||
std::ifstream file(filePath);
|
||||
if (!file.good()) {
|
||||
if (verbose) {
|
||||
std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl;
|
||||
std::cout << "Warning: Unable to open " << filename.c_str() << " file" << '\n';
|
||||
}
|
||||
animatedSprite_t as;
|
||||
as.texture = texture;
|
||||
@@ -108,7 +112,7 @@ animatedSprite_t loadAnimationFromFile(Texture *texture, const std::string &file
|
||||
}
|
||||
|
||||
// Carga la animación desde bytes en memoria
|
||||
animatedSprite_t loadAnimationFromMemory(Texture *texture, const std::vector<uint8_t> &bytes, const std::string &nameForLogs, bool verbose) {
|
||||
auto loadAnimationFromMemory(Texture *texture, const std::vector<uint8_t> &bytes, const std::string &nameForLogs, bool verbose) -> animatedSprite_t {
|
||||
if (bytes.empty()) {
|
||||
animatedSprite_t as;
|
||||
as.texture = texture;
|
||||
@@ -134,7 +138,7 @@ AnimatedSprite::AnimatedSprite(Texture *texture, SDL_Renderer *renderer, const s
|
||||
animation.insert(animation.end(), as.animations.begin(), as.animations.end());
|
||||
}
|
||||
|
||||
else if (buffer) {
|
||||
else if (buffer != nullptr) {
|
||||
loadFromVector(buffer);
|
||||
}
|
||||
}
|
||||
@@ -159,7 +163,7 @@ AnimatedSprite::~AnimatedSprite() {
|
||||
}
|
||||
|
||||
// Obtiene el indice de la animación a partir del nombre
|
||||
int AnimatedSprite::getIndex(const std::string &name) {
|
||||
auto AnimatedSprite::getIndex(const std::string &name) -> int {
|
||||
int index = -1;
|
||||
|
||||
for (const auto &a : animation) {
|
||||
@@ -169,7 +173,7 @@ int AnimatedSprite::getIndex(const std::string &name) {
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "** Warning: could not find \"" << name.c_str() << "\" animation" << std::endl;
|
||||
std::cout << "** Warning: could not find \"" << name.c_str() << "\" animation" << '\n';
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -205,7 +209,7 @@ void AnimatedSprite::animate() {
|
||||
}
|
||||
|
||||
// Obtiene el numero de frames de la animación actual
|
||||
int AnimatedSprite::getNumFrames() {
|
||||
auto AnimatedSprite::getNumFrames() -> int {
|
||||
return (int)animation[currentAnimation].frames.size();
|
||||
}
|
||||
|
||||
@@ -260,22 +264,22 @@ void AnimatedSprite::setAnimationCompleted(int index, bool value) {
|
||||
}
|
||||
|
||||
// Comprueba si ha terminado la animación
|
||||
bool AnimatedSprite::animationIsCompleted() {
|
||||
auto AnimatedSprite::animationIsCompleted() -> bool {
|
||||
return animation[currentAnimation].completed;
|
||||
}
|
||||
|
||||
// Devuelve el rectangulo de una animación y frame concreto
|
||||
SDL_Rect AnimatedSprite::getAnimationClip(const std::string &name, Uint8 index) {
|
||||
auto AnimatedSprite::getAnimationClip(const std::string &name, Uint8 index) -> SDL_Rect {
|
||||
return animation[getIndex(name)].frames[index];
|
||||
}
|
||||
|
||||
// Devuelve el rectangulo de una animación y frame concreto
|
||||
SDL_Rect AnimatedSprite::getAnimationClip(int indexA, Uint8 indexF) {
|
||||
auto AnimatedSprite::getAnimationClip(int indexA, Uint8 indexF) -> SDL_Rect {
|
||||
return animation[indexA].frames[indexF];
|
||||
}
|
||||
|
||||
// Carga la animación desde un vector
|
||||
bool AnimatedSprite::loadFromVector(std::vector<std::string> *source) {
|
||||
auto AnimatedSprite::loadFromVector(std::vector<std::string> *source) -> bool {
|
||||
// Inicializa variables
|
||||
int framesPerRow = 0;
|
||||
int frameWidth = 0;
|
||||
@@ -307,10 +311,10 @@ bool AnimatedSprite::loadFromVector(std::vector<std::string> *source) {
|
||||
line = source->at(index);
|
||||
|
||||
// Encuentra la posición del caracter '='
|
||||
int pos = line.find("=");
|
||||
int pos = line.find('=');
|
||||
|
||||
// Procesa las dos subcadenas
|
||||
if (pos != (int)line.npos) {
|
||||
if (pos != (int)std::string::npos) {
|
||||
if (line.substr(0, pos) == "name") {
|
||||
buffer.name = line.substr(pos + 1, line.length());
|
||||
}
|
||||
@@ -338,7 +342,7 @@ bool AnimatedSprite::loadFromVector(std::vector<std::string> *source) {
|
||||
}
|
||||
|
||||
else {
|
||||
std::cout << "Warning: unknown parameter " << line.substr(0, pos).c_str() << std::endl;
|
||||
std::cout << "Warning: unknown parameter " << line.substr(0, pos).c_str() << '\n';
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
@@ -351,10 +355,10 @@ bool AnimatedSprite::loadFromVector(std::vector<std::string> *source) {
|
||||
// En caso contrario se parsea el fichero para buscar las variables y los valores
|
||||
else {
|
||||
// Encuentra la posición del caracter '='
|
||||
int pos = line.find("=");
|
||||
int pos = line.find('=');
|
||||
|
||||
// Procesa las dos subcadenas
|
||||
if (pos != (int)line.npos) {
|
||||
if (pos != (int)std::string::npos) {
|
||||
if (line.substr(0, pos) == "framesPerRow") {
|
||||
framesPerRow = std::stoi(line.substr(pos + 1, line.length()));
|
||||
}
|
||||
@@ -368,7 +372,7 @@ bool AnimatedSprite::loadFromVector(std::vector<std::string> *source) {
|
||||
}
|
||||
|
||||
else {
|
||||
std::cout << "Warning: unknown parameter " << line.substr(0, pos).c_str() << std::endl;
|
||||
std::cout << "Warning: unknown parameter " << line.substr(0, pos).c_str() << '\n';
|
||||
success = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ struct animatedSprite_t {
|
||||
};
|
||||
|
||||
// Carga la animación desde un fichero
|
||||
animatedSprite_t loadAnimationFromFile(Texture *texture, const std::string &filePath, bool verbose = false);
|
||||
auto loadAnimationFromFile(Texture *texture, const std::string &filePath, bool verbose = false) -> animatedSprite_t;
|
||||
|
||||
// Carga la animación desde bytes en memoria
|
||||
animatedSprite_t loadAnimationFromMemory(Texture *texture, const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "", bool verbose = false);
|
||||
auto loadAnimationFromMemory(Texture *texture, const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "", bool verbose = false) -> animatedSprite_t;
|
||||
|
||||
class AnimatedSprite : public MovingSprite {
|
||||
private:
|
||||
@@ -48,7 +48,7 @@ class AnimatedSprite : public MovingSprite {
|
||||
void animate();
|
||||
|
||||
// Obtiene el numero de frames de la animación actual
|
||||
int getNumFrames();
|
||||
auto getNumFrames() -> int;
|
||||
|
||||
// Establece el frame actual de la animación
|
||||
void setCurrentFrame(int num);
|
||||
@@ -69,17 +69,17 @@ class AnimatedSprite : public MovingSprite {
|
||||
void setAnimationCompleted(int index, bool value);
|
||||
|
||||
// Comprueba si ha terminado la animación
|
||||
bool animationIsCompleted();
|
||||
auto animationIsCompleted() -> bool;
|
||||
|
||||
// Devuelve el rectangulo de una animación y frame concreto
|
||||
SDL_Rect getAnimationClip(const std::string &name = "default", Uint8 index = 0);
|
||||
SDL_Rect getAnimationClip(int indexA = 0, Uint8 indexF = 0);
|
||||
auto getAnimationClip(const std::string &name = "default", Uint8 index = 0) -> SDL_Rect;
|
||||
auto getAnimationClip(int indexA = 0, Uint8 indexF = 0) -> SDL_Rect;
|
||||
|
||||
// Obtiene el indice de la animación a partir del nombre
|
||||
int getIndex(const std::string &name);
|
||||
auto getIndex(const std::string &name) -> int;
|
||||
|
||||
// Carga la animación desde un vector
|
||||
bool loadFromVector(std::vector<std::string> *source);
|
||||
auto loadFromVector(std::vector<std::string> *source) -> bool;
|
||||
|
||||
// Establece la animacion actual
|
||||
void setCurrentAnimation(const std::string &name = "default");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "core/rendering/fade.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdlib.h> // for rand
|
||||
|
||||
#include <cstdlib> // for rand
|
||||
#include <iostream> // for char_traits, basic_ostream, operator<<
|
||||
|
||||
#include "game/defaults.hpp" // for GAMECANVAS_HEIGHT, GAMECANVAS_WIDTH
|
||||
@@ -15,7 +15,7 @@ Fade::Fade(SDL_Renderer *renderer)
|
||||
SDL_SetTextureScaleMode(mBackbuffer, SDL_SCALEMODE_NEAREST);
|
||||
}
|
||||
if (mBackbuffer == nullptr) {
|
||||
std::cout << "Error: textTexture could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||
std::cout << "Error: textTexture could not be created!\nSDL Error: " << SDL_GetError() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,8 +92,9 @@ void Fade::render() {
|
||||
SDL_RenderFillRect(mRenderer, &fR2);
|
||||
}
|
||||
|
||||
if ((mCounter * 4) > GAMECANVAS_HEIGHT)
|
||||
if ((mCounter * 4) > GAMECANVAS_HEIGHT) {
|
||||
mFinished = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -145,8 +146,9 @@ void Fade::render() {
|
||||
|
||||
// Actualiza las variables internas
|
||||
void Fade::update() {
|
||||
if (mEnabled)
|
||||
if (mEnabled) {
|
||||
mCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
// Activa el fade
|
||||
@@ -163,12 +165,12 @@ void Fade::activateFade() {
|
||||
}
|
||||
|
||||
// Comprueba si está activo
|
||||
bool Fade::isEnabled() {
|
||||
auto Fade::isEnabled() const -> bool {
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
// Comprueba si ha terminado la transicion
|
||||
bool Fade::hasEnded() {
|
||||
auto Fade::hasEnded() const -> bool {
|
||||
return mFinished;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ class Fade {
|
||||
void activateFade();
|
||||
|
||||
// Comprueba si ha terminado la transicion
|
||||
bool hasEnded();
|
||||
[[nodiscard]] auto hasEnded() const -> bool;
|
||||
|
||||
// Comprueba si está activo
|
||||
bool isEnabled();
|
||||
[[nodiscard]] auto isEnabled() const -> bool;
|
||||
|
||||
// Establece el tipo de fade
|
||||
void setFadeType(Uint8 fadeType);
|
||||
|
||||
@@ -12,31 +12,22 @@ MovingSprite::MovingSprite(float x, float y, int w, int h, float velx, float vel
|
||||
vx(velx),
|
||||
vy(vely),
|
||||
ax(accelx),
|
||||
ay(accely),
|
||||
zoomW(1),
|
||||
zoomH(1),
|
||||
angle(0.0),
|
||||
rotateEnabled(false),
|
||||
rotateSpeed(0),
|
||||
rotateAmount(0.0),
|
||||
counter(0),
|
||||
center(nullptr),
|
||||
currentFlip(SDL_FLIP_NONE) {
|
||||
ay(accely) {
|
||||
}
|
||||
|
||||
// Reinicia todas las variables
|
||||
void MovingSprite::clear() {
|
||||
x = 0.0f; // Posición en el eje X
|
||||
y = 0.0f; // Posición en el eje Y
|
||||
x = 0.0F; // Posición en el eje X
|
||||
y = 0.0F; // Posición en el eje Y
|
||||
|
||||
vx = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
|
||||
vy = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse
|
||||
vx = 0.0F; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
|
||||
vy = 0.0F; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse
|
||||
|
||||
ax = 0.0f; // Aceleración en el eje X. Variación de la velocidad
|
||||
ay = 0.0f; // Aceleración en el eje Y. Variación de la velocidad
|
||||
ax = 0.0F; // Aceleración en el eje X. Variación de la velocidad
|
||||
ay = 0.0F; // Aceleración en el eje Y. Variación de la velocidad
|
||||
|
||||
zoomW = 1.0f; // Zoom aplicado a la anchura
|
||||
zoomH = 1.0f; // Zoom aplicado a la altura
|
||||
zoomW = 1.0F; // Zoom aplicado a la anchura
|
||||
zoomH = 1.0F; // Zoom aplicado a la altura
|
||||
|
||||
angle = 0.0; // Angulo para dibujarlo
|
||||
rotateEnabled = false; // Indica si ha de rotar
|
||||
@@ -71,48 +62,48 @@ void MovingSprite::render() {
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
float MovingSprite::getPosX() {
|
||||
auto MovingSprite::getPosX() const -> float {
|
||||
return x;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
float MovingSprite::getPosY() {
|
||||
auto MovingSprite::getPosY() const -> float {
|
||||
return y;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
float MovingSprite::getVelX() {
|
||||
auto MovingSprite::getVelX() const -> float {
|
||||
return vx;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
float MovingSprite::getVelY() {
|
||||
auto MovingSprite::getVelY() const -> float {
|
||||
return vy;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
float MovingSprite::getAccelX() {
|
||||
auto MovingSprite::getAccelX() const -> float {
|
||||
return ax;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
float MovingSprite::getAccelY() {
|
||||
auto MovingSprite::getAccelY() const -> float {
|
||||
return ay;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
float MovingSprite::getZoomW() {
|
||||
auto MovingSprite::getZoomW() const -> float {
|
||||
return zoomW;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
float MovingSprite::getZoomH() {
|
||||
auto MovingSprite::getZoomH() const -> float {
|
||||
return zoomH;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
double MovingSprite::getAngle() {
|
||||
auto MovingSprite::getAngle() const -> double {
|
||||
return angle;
|
||||
}
|
||||
|
||||
@@ -180,23 +171,24 @@ void MovingSprite::decAngle(double value) {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool MovingSprite::getRotate() {
|
||||
auto MovingSprite::getRotate() const -> bool {
|
||||
return rotateEnabled;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
Uint16 MovingSprite::getRotateSpeed() {
|
||||
auto MovingSprite::getRotateSpeed() const -> Uint16 {
|
||||
return rotateSpeed;
|
||||
}
|
||||
|
||||
// Establece la rotacion
|
||||
void MovingSprite::rotate() {
|
||||
if (enabled)
|
||||
if (enabled) {
|
||||
if (rotateEnabled) {
|
||||
if (counter % rotateSpeed == 0) {
|
||||
incAngle(rotateAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
@@ -250,12 +242,12 @@ void MovingSprite::flip() {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
SDL_FlipMode MovingSprite::getFlip() {
|
||||
auto MovingSprite::getFlip() -> SDL_FlipMode {
|
||||
return currentFlip;
|
||||
}
|
||||
|
||||
// Devuelve el rectangulo donde está el sprite
|
||||
SDL_Rect MovingSprite::getRect() {
|
||||
auto MovingSprite::getRect() -> SDL_Rect {
|
||||
const SDL_Rect rect = {(int)x, (int)y, w, h};
|
||||
return rect;
|
||||
}
|
||||
@@ -278,10 +270,10 @@ void MovingSprite::undoMoveY() {
|
||||
|
||||
// Pone a cero las velocidades de desplacamiento
|
||||
void MovingSprite::clearVel() {
|
||||
vx = vy = 0.0f;
|
||||
vx = vy = 0.0F;
|
||||
}
|
||||
|
||||
// Devuelve el incremento en el eje X en pixels
|
||||
int MovingSprite::getIncX() {
|
||||
auto MovingSprite::getIncX() const -> int {
|
||||
return (int)x - (int)xPrev;
|
||||
}
|
||||
@@ -22,16 +22,16 @@ class MovingSprite : public Sprite {
|
||||
float ax; // Aceleración en el eje X. Variación de la velocidad
|
||||
float ay; // Aceleración en el eje Y. Variación de la velocidad
|
||||
|
||||
float zoomW; // Zoom aplicado a la anchura
|
||||
float zoomH; // Zoom aplicado a la altura
|
||||
float zoomW{1}; // Zoom aplicado a la anchura
|
||||
float zoomH{1}; // Zoom aplicado a la altura
|
||||
|
||||
double angle; // Angulo para dibujarlo
|
||||
bool rotateEnabled; // Indica si ha de rotar
|
||||
int rotateSpeed; // Velocidad de giro
|
||||
double rotateAmount; // Cantidad de grados a girar en cada iteración
|
||||
int counter; // Contador interno
|
||||
SDL_Point *center; // Centro de rotación
|
||||
SDL_FlipMode currentFlip; // Indica como se voltea el sprite
|
||||
double angle{0.0}; // Angulo para dibujarlo
|
||||
bool rotateEnabled{false}; // Indica si ha de rotar
|
||||
int rotateSpeed{0}; // Velocidad de giro
|
||||
double rotateAmount{0.0}; // Cantidad de grados a girar en cada iteración
|
||||
int counter{0}; // Contador interno
|
||||
SDL_Point *center{nullptr}; // Centro de rotación
|
||||
SDL_FlipMode currentFlip{SDL_FLIP_NONE}; // Indica como se voltea el sprite
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
@@ -54,38 +54,38 @@ class MovingSprite : public Sprite {
|
||||
|
||||
// Obten el valor de la variable
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
float getPosX();
|
||||
[[nodiscard]] auto getPosX() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
// cppcheck-suppress duplInheritedMember
|
||||
float getPosY();
|
||||
[[nodiscard]] auto getPosY() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
float getVelX();
|
||||
[[nodiscard]] auto getVelX() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
float getVelY();
|
||||
[[nodiscard]] auto getVelY() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
float getAccelX();
|
||||
[[nodiscard]] auto getAccelX() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
float getAccelY();
|
||||
[[nodiscard]] auto getAccelY() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
float getZoomW();
|
||||
[[nodiscard]] auto getZoomW() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
float getZoomH();
|
||||
[[nodiscard]] auto getZoomH() const -> float;
|
||||
|
||||
// Obten el valor de la variable
|
||||
double getAngle();
|
||||
[[nodiscard]] auto getAngle() const -> double;
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool getRotate();
|
||||
[[nodiscard]] auto getRotate() const -> bool;
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
Uint16 getRotateSpeed();
|
||||
[[nodiscard]] auto getRotateSpeed() const -> Uint16;
|
||||
|
||||
// Establece la posición y el tamaño del objeto
|
||||
void setRect(SDL_Rect rect) override;
|
||||
@@ -115,7 +115,7 @@ class MovingSprite : public Sprite {
|
||||
void setZoomH(float value);
|
||||
|
||||
// Establece el valor de la variable
|
||||
void setAngle(double vaue);
|
||||
void setAngle(double value);
|
||||
|
||||
// Incrementa el valor de la variable
|
||||
void incAngle(double value);
|
||||
@@ -145,10 +145,10 @@ class MovingSprite : public Sprite {
|
||||
void flip();
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
SDL_FlipMode getFlip();
|
||||
auto getFlip() -> SDL_FlipMode;
|
||||
|
||||
// Devuelve el rectangulo donde está el sprite
|
||||
SDL_Rect getRect() override;
|
||||
auto getRect() -> SDL_Rect override;
|
||||
|
||||
// Deshace el último movimiento
|
||||
void undoMove();
|
||||
@@ -163,5 +163,5 @@ class MovingSprite : public Sprite {
|
||||
void clearVel();
|
||||
|
||||
// Devuelve el incremento en el eje X en pixels
|
||||
int getIncX();
|
||||
[[nodiscard]] auto getIncX() const -> int;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <algorithm> // for max, min
|
||||
#include <cmath> // for lround
|
||||
#include <cstring> // for memcpy
|
||||
#include <iostream> // for basic_ostream, operator<<, cout, endl
|
||||
#include <string> // for basic_string, char_traits, string
|
||||
@@ -108,7 +109,7 @@ Screen::Screen(SDL_Window *window, SDL_Renderer *renderer)
|
||||
}
|
||||
if (gameCanvas == nullptr) {
|
||||
if (Options::settings.console) {
|
||||
std::cout << "gameCanvas could not be created!\nSDL Error: " << SDL_GetError() << std::endl;
|
||||
std::cout << "gameCanvas could not be created!\nSDL Error: " << SDL_GetError() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +352,7 @@ void Screen::applyFullscreen(bool fullscreen) {
|
||||
void Screen::applyWindowedLayout() {
|
||||
windowWidth = gameCanvasWidth;
|
||||
windowHeight = gameCanvasHeight;
|
||||
dest = {0, 0, gameCanvasWidth, gameCanvasHeight};
|
||||
dest = {.x = 0, .y = 0, .w = gameCanvasWidth, .h = gameCanvasHeight};
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
windowWidth *= WASM_RENDER_SCALE;
|
||||
@@ -396,12 +397,12 @@ void Screen::computeFullscreenGameRect() {
|
||||
float ratio = (float)gameCanvasWidth / (float)gameCanvasHeight;
|
||||
if ((windowWidth - gameCanvasWidth) >= (windowHeight - gameCanvasHeight)) {
|
||||
dest.h = windowHeight;
|
||||
dest.w = (int)((windowHeight * ratio) + 0.5f);
|
||||
dest.w = static_cast<int>(std::lround(windowHeight * ratio));
|
||||
dest.x = (windowWidth - dest.w) / 2;
|
||||
dest.y = (windowHeight - dest.h) / 2;
|
||||
} else {
|
||||
dest.w = windowWidth;
|
||||
dest.h = (int)((windowWidth / ratio) + 0.5f);
|
||||
dest.h = static_cast<int>(std::lround(windowWidth / ratio));
|
||||
dest.x = (windowWidth - dest.w) / 2;
|
||||
dest.y = (windowHeight - dest.h) / 2;
|
||||
}
|
||||
@@ -579,7 +580,7 @@ void Screen::toggleShaderEnabled() {
|
||||
setShaderEnabled(!Options::video.shader.enabled);
|
||||
}
|
||||
|
||||
auto Screen::isShaderEnabled() const -> bool {
|
||||
auto Screen::isShaderEnabled() -> bool {
|
||||
return Options::video.shader.enabled;
|
||||
}
|
||||
|
||||
@@ -595,7 +596,7 @@ void Screen::setActiveShader(Rendering::ShaderType type) {
|
||||
notify(type == Rendering::ShaderType::CRTPI ? "Shader: CRTPI" : "Shader: POSTFX", MAGENTA, BLACK, DUR_MS);
|
||||
}
|
||||
|
||||
auto Screen::getActiveShader() const -> Rendering::ShaderType {
|
||||
auto Screen::getActiveShader() -> Rendering::ShaderType {
|
||||
return Options::video.shader.current_shader;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -46,7 +46,7 @@ class Screen {
|
||||
void setVideoMode(bool fullscreen); // Establece el modo de video
|
||||
void toggleVideoMode(); // Cambia entre pantalla completa y ventana
|
||||
void handleCanvasResized(); // En Emscripten, reaplica setVideoMode tras un cambio del navegador (salida de fullscreen con Esc, rotación). No-op fuera de Emscripten
|
||||
void syncFullscreenFlagFromBrowser(bool isFullscreen); // Sincroniza el flag interno de fullscreen con el estado real del navegador. Debe llamarse antes de diferir handleCanvasResized. No-op fuera de Emscripten
|
||||
static void syncFullscreenFlagFromBrowser(bool isFullscreen); // Sincroniza el flag interno de fullscreen con el estado real del navegador. Debe llamarse antes de diferir handleCanvasResized. No-op fuera de Emscripten
|
||||
void toggleIntegerScale(); // Alterna el escalado entero
|
||||
void setIntegerScale(bool enabled); // Establece el escalado entero
|
||||
void toggleVSync(); // Alterna el V-Sync
|
||||
@@ -66,13 +66,13 @@ class Screen {
|
||||
// GPU / shaders (post-procesado). En builds con NO_SHADERS (Emscripten) son no-op.
|
||||
void initShaders(); // Crea el backend GPU si no existe y lo inicializa
|
||||
void shutdownShaders(); // Libera el backend GPU
|
||||
auto isGpuAccelerated() const -> bool; // true si el backend existe y reporta hardware OK
|
||||
[[nodiscard]] auto isGpuAccelerated() const -> bool; // true si el backend existe y reporta hardware OK
|
||||
void setShaderEnabled(bool enabled); // Activa o desactiva el post-procesado (persiste)
|
||||
void toggleShaderEnabled(); // Alterna post-procesado
|
||||
auto isShaderEnabled() const -> bool; // Estado actual (lee options)
|
||||
[[nodiscard]] static auto isShaderEnabled() -> bool; // Estado actual (lee options)
|
||||
#ifndef NO_SHADERS
|
||||
void setActiveShader(Rendering::ShaderType type); // POSTFX o CRTPI
|
||||
auto getActiveShader() const -> Rendering::ShaderType;
|
||||
[[nodiscard]] static auto getActiveShader() -> Rendering::ShaderType;
|
||||
#endif
|
||||
void toggleActiveShader(); // Alterna POSTFX ↔ CRTPI
|
||||
|
||||
@@ -80,7 +80,7 @@ class Screen {
|
||||
// Retornen false si GPU off / shaders off / llista buida (igual que a aee_plus).
|
||||
auto nextPreset() -> bool;
|
||||
auto prevPreset() -> bool;
|
||||
auto getCurrentPresetName() const -> const char *;
|
||||
[[nodiscard]] auto getCurrentPresetName() const -> const char *;
|
||||
void applyCurrentPostFXPreset(); // Escriu el preset PostFX actiu al backend
|
||||
void applyCurrentCrtPiPreset(); // Escriu el preset CrtPi actiu al backend
|
||||
|
||||
|
||||
@@ -762,7 +762,7 @@ namespace Rendering {
|
||||
}
|
||||
|
||||
// Copia directa — el upscale lo hace la GPU en el primer render pass
|
||||
std::memcpy(mapped, pixels, static_cast<size_t>(width * height * 4));
|
||||
std::memcpy(mapped, pixels, static_cast<size_t>(width) * height * 4);
|
||||
|
||||
SDL_UnmapGPUTransferBuffer(device_, upload_buffer_);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
/** @brief Identificador del shader de post-procesado activo */
|
||||
enum class ShaderType { POSTFX,
|
||||
enum class ShaderType : std::uint8_t { POSTFX,
|
||||
CRTPI };
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,8 +27,8 @@ void SmartSprite::init() {
|
||||
// Actualiza la posición y comprueba si ha llegado a su destino
|
||||
void SmartSprite::update() {
|
||||
if (enabled) {
|
||||
// Actualiza las variables internas del objeto
|
||||
MovingSprite::update();
|
||||
// Actualiza animació + posició (delegant en AnimatedSprite::update)
|
||||
AnimatedSprite::update();
|
||||
|
||||
// Comprueba el movimiento
|
||||
checkMove();
|
||||
@@ -47,7 +47,7 @@ void SmartSprite::render() {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int SmartSprite::getEnabledCounter() {
|
||||
auto SmartSprite::getEnabledCounter() const -> int {
|
||||
return enabledCounter;
|
||||
}
|
||||
|
||||
@@ -67,12 +67,12 @@ void SmartSprite::setDestY(int y) {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int SmartSprite::getDestX() {
|
||||
auto SmartSprite::getDestX() const -> int {
|
||||
return destX;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int SmartSprite::getDestY() {
|
||||
auto SmartSprite::getDestY() const -> int {
|
||||
return destY;
|
||||
}
|
||||
|
||||
@@ -86,8 +86,8 @@ void SmartSprite::checkMove() {
|
||||
setPosX(destX);
|
||||
|
||||
// Lo detiene
|
||||
setVelX(0.0f);
|
||||
setAccelX(0.0f);
|
||||
setVelX(0.0F);
|
||||
setAccelX(0.0F);
|
||||
}
|
||||
}
|
||||
// Comprueba si se desplaza en el eje X hacia la izquierda
|
||||
@@ -98,8 +98,8 @@ void SmartSprite::checkMove() {
|
||||
setPosX(destX);
|
||||
|
||||
// Lo detiene
|
||||
setVelX(0.0f);
|
||||
setAccelX(0.0f);
|
||||
setVelX(0.0F);
|
||||
setAccelX(0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,8 @@ void SmartSprite::checkMove() {
|
||||
setPosY(destY);
|
||||
|
||||
// Lo detiene
|
||||
setVelY(0.0f);
|
||||
setAccelY(0.0f);
|
||||
setVelY(0.0F);
|
||||
setAccelY(0.0F);
|
||||
}
|
||||
}
|
||||
// Comprueba si se desplaza en el eje Y hacia arriba
|
||||
@@ -123,8 +123,8 @@ void SmartSprite::checkMove() {
|
||||
setPosY(destY);
|
||||
|
||||
// Lo detiene
|
||||
setVelY(0.0f);
|
||||
setAccelY(0.0f);
|
||||
setVelY(0.0F);
|
||||
setAccelY(0.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ void SmartSprite::checkMove() {
|
||||
// Comprueba si ha terminado
|
||||
void SmartSprite::checkFinished() {
|
||||
// Comprueba si ha llegado a su destino
|
||||
onDestination = (getPosX() == destX && getPosY() == destY) ? true : false;
|
||||
onDestination = getPosX() == destX && getPosY() == destY;
|
||||
|
||||
if (onDestination) { // Si esta en el destino comprueba su contador
|
||||
if (enabledCounter == 0) { // Si ha llegado a cero, deshabilita el objeto y lo marca como finalizado
|
||||
@@ -144,11 +144,11 @@ void SmartSprite::checkFinished() {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool SmartSprite::isOnDestination() {
|
||||
auto SmartSprite::isOnDestination() const -> bool {
|
||||
return onDestination;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool SmartSprite::hasFinished() {
|
||||
auto SmartSprite::hasFinished() const -> bool {
|
||||
return finished;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class SmartSprite : public AnimatedSprite {
|
||||
void render() override;
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int getEnabledCounter();
|
||||
[[nodiscard]] auto getEnabledCounter() const -> int;
|
||||
|
||||
// Establece el valor de la variable
|
||||
void setEnabledCounter(int value);
|
||||
@@ -47,14 +47,14 @@ class SmartSprite : public AnimatedSprite {
|
||||
void setDestY(int y);
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int getDestX();
|
||||
[[nodiscard]] auto getDestX() const -> int;
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int getDestY();
|
||||
[[nodiscard]] auto getDestY() const -> int;
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool isOnDestination();
|
||||
[[nodiscard]] auto isOnDestination() const -> bool;
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool hasFinished();
|
||||
[[nodiscard]] auto hasFinished() const -> bool;
|
||||
};
|
||||
|
||||
@@ -39,22 +39,22 @@ void Sprite::render() {
|
||||
}
|
||||
|
||||
// Obten el valor de la variable
|
||||
int Sprite::getPosX() {
|
||||
auto Sprite::getPosX() const -> int {
|
||||
return x;
|
||||
}
|
||||
|
||||
// Obten el valor de la variable
|
||||
int Sprite::getPosY() {
|
||||
auto Sprite::getPosY() const -> int {
|
||||
return y;
|
||||
}
|
||||
|
||||
// Obten el valor de la variable
|
||||
int Sprite::getWidth() {
|
||||
auto Sprite::getWidth() const -> int {
|
||||
return w;
|
||||
}
|
||||
|
||||
// Obten el valor de la variable
|
||||
int Sprite::getHeight() {
|
||||
auto Sprite::getHeight() const -> int {
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ void Sprite::setHeight(int h) {
|
||||
}
|
||||
|
||||
// Obten el valor de la variable
|
||||
SDL_Rect Sprite::getSpriteClip() {
|
||||
auto Sprite::getSpriteClip() -> SDL_Rect {
|
||||
return spriteClip;
|
||||
}
|
||||
|
||||
@@ -96,11 +96,11 @@ void Sprite::setSpriteClip(SDL_Rect rect) {
|
||||
|
||||
// Establece el valor de la variable
|
||||
void Sprite::setSpriteClip(int x, int y, int w, int h) {
|
||||
spriteClip = {x, y, w, h};
|
||||
spriteClip = {.x = x, .y = y, .w = w, .h = h};
|
||||
}
|
||||
|
||||
// Obten el valor de la variable
|
||||
Texture *Sprite::getTexture() {
|
||||
auto Sprite::getTexture() -> Texture * {
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ void Sprite::setTexture(Texture *texture) {
|
||||
}
|
||||
|
||||
// Obten el valor de la variable
|
||||
SDL_Renderer *Sprite::getRenderer() {
|
||||
auto Sprite::getRenderer() -> SDL_Renderer * {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@@ -125,12 +125,12 @@ void Sprite::setEnabled(bool value) {
|
||||
}
|
||||
|
||||
// Comprueba si el objeto está habilitado
|
||||
bool Sprite::isEnabled() {
|
||||
auto Sprite::isEnabled() -> bool {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
// Devuelve el rectangulo donde está el sprite
|
||||
SDL_Rect Sprite::getRect() {
|
||||
auto Sprite::getRect() -> SDL_Rect {
|
||||
SDL_Rect rect = {x, y, w, h};
|
||||
return rect;
|
||||
}
|
||||
|
||||
@@ -29,16 +29,16 @@ class Sprite {
|
||||
virtual void render();
|
||||
|
||||
// Obten el valor de la variable
|
||||
int getPosX();
|
||||
[[nodiscard]] auto getPosX() const -> int;
|
||||
|
||||
// Obten el valor de la variable
|
||||
int getPosY();
|
||||
[[nodiscard]] auto getPosY() const -> int;
|
||||
|
||||
// Obten el valor de la variable
|
||||
int getWidth();
|
||||
[[nodiscard]] auto getWidth() const -> int;
|
||||
|
||||
// Obten el valor de la variable
|
||||
int getHeight();
|
||||
[[nodiscard]] auto getHeight() const -> int;
|
||||
|
||||
// Establece la posición del objeto
|
||||
void setPos(SDL_Rect rect);
|
||||
@@ -56,7 +56,7 @@ class Sprite {
|
||||
void setHeight(int h);
|
||||
|
||||
// Obten el valor de la variable
|
||||
SDL_Rect getSpriteClip();
|
||||
auto getSpriteClip() -> SDL_Rect;
|
||||
|
||||
// Establece el valor de la variable
|
||||
void setSpriteClip(SDL_Rect rect);
|
||||
@@ -65,13 +65,13 @@ class Sprite {
|
||||
void setSpriteClip(int x, int y, int w, int h);
|
||||
|
||||
// Obten el valor de la variable
|
||||
Texture *getTexture();
|
||||
auto getTexture() -> Texture *;
|
||||
|
||||
// Establece el valor de la variable
|
||||
void setTexture(Texture *texture);
|
||||
|
||||
// Obten el valor de la variable
|
||||
SDL_Renderer *getRenderer();
|
||||
auto getRenderer() -> SDL_Renderer *;
|
||||
|
||||
// Establece el valor de la variable
|
||||
void setRenderer(SDL_Renderer *renderer);
|
||||
@@ -80,10 +80,10 @@ class Sprite {
|
||||
virtual void setEnabled(bool value);
|
||||
|
||||
// Comprueba si el objeto está habilitado
|
||||
virtual bool isEnabled();
|
||||
virtual auto isEnabled() -> bool;
|
||||
|
||||
// Devuelve el rectangulo donde está el sprite
|
||||
virtual SDL_Rect getRect();
|
||||
virtual auto getRect() -> SDL_Rect;
|
||||
|
||||
// Establece los valores de posición y tamaño del sprite
|
||||
virtual void setRect(SDL_Rect rect);
|
||||
|
||||
@@ -39,25 +39,25 @@ static void computeTextFileOffsets(textFile_t &tf) {
|
||||
}
|
||||
|
||||
// Llena una estructuta textFile_t desde un fichero
|
||||
textFile_t LoadTextFile(const std::string &file, bool verbose) {
|
||||
auto LoadTextFile(const std::string &file, bool verbose) -> textFile_t {
|
||||
textFile_t tf;
|
||||
tf.boxWidth = 0;
|
||||
tf.boxHeight = 0;
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
tf.offset[i].x = 0;
|
||||
tf.offset[i].y = 0;
|
||||
tf.offset[i].w = 0;
|
||||
for (auto &i : tf.offset) {
|
||||
i.x = 0;
|
||||
i.y = 0;
|
||||
i.w = 0;
|
||||
}
|
||||
|
||||
const std::string filename = file.substr(file.find_last_of("\\/") + 1).c_str();
|
||||
const std::string filename = file.substr(file.find_last_of("\\/") + 1);
|
||||
std::ifstream rfile(file);
|
||||
if (rfile.is_open() && rfile.good()) {
|
||||
parseTextFileStream(rfile, tf);
|
||||
if (verbose) {
|
||||
std::cout << "Text loaded: " << filename.c_str() << std::endl;
|
||||
std::cout << "Text loaded: " << filename.c_str() << '\n';
|
||||
}
|
||||
} else if (verbose) {
|
||||
std::cout << "Warning: Unable to open " << filename.c_str() << " file" << std::endl;
|
||||
std::cout << "Warning: Unable to open " << filename.c_str() << " file" << '\n';
|
||||
}
|
||||
|
||||
computeTextFileOffsets(tf);
|
||||
@@ -65,21 +65,21 @@ textFile_t LoadTextFile(const std::string &file, bool verbose) {
|
||||
}
|
||||
|
||||
// Llena una estructura textFile_t desde bytes en memoria
|
||||
textFile_t LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose) {
|
||||
auto LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose) -> textFile_t {
|
||||
textFile_t tf;
|
||||
tf.boxWidth = 0;
|
||||
tf.boxHeight = 0;
|
||||
for (int i = 0; i < 128; ++i) {
|
||||
tf.offset[i].x = 0;
|
||||
tf.offset[i].y = 0;
|
||||
tf.offset[i].w = 0;
|
||||
for (auto &i : tf.offset) {
|
||||
i.x = 0;
|
||||
i.y = 0;
|
||||
i.w = 0;
|
||||
}
|
||||
if (!bytes.empty()) {
|
||||
std::string content(reinterpret_cast<const char *>(bytes.data()), bytes.size());
|
||||
std::stringstream ss(content);
|
||||
parseTextFileStream(ss, tf);
|
||||
if (verbose) {
|
||||
std::cout << "Text loaded from memory" << std::endl;
|
||||
std::cout << "Text loaded from memory" << '\n';
|
||||
}
|
||||
}
|
||||
computeTextFileOffsets(tf);
|
||||
@@ -170,9 +170,8 @@ Text::Text(const std::vector<uint8_t> &pngBytes, const std::vector<uint8_t> &txt
|
||||
// Destructor
|
||||
Text::~Text() {
|
||||
delete sprite;
|
||||
if (texture != nullptr) {
|
||||
|
||||
delete texture;
|
||||
}
|
||||
}
|
||||
|
||||
// Escribe texto en pantalla
|
||||
@@ -187,11 +186,11 @@ void Text::write(int x, int y, const std::string &text, int kerning, int lenght)
|
||||
const int width = sprite->getWidth();
|
||||
const int height = sprite->getHeight();
|
||||
for (int i = 0; i < lenght; ++i) {
|
||||
const int index = text[i];
|
||||
const int index = static_cast<unsigned char>(text[i]);
|
||||
sprite->setSpriteClip(offset[index].x, offset[index].y, width, height);
|
||||
sprite->setPosX(x + shift);
|
||||
sprite->render();
|
||||
shift += fixedWidth ? boxWidth : (offset[int(text[i])].w + kerning);
|
||||
shift += fixedWidth ? boxWidth : (offset[index].w + kerning);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,18 +248,19 @@ void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerni
|
||||
}
|
||||
|
||||
// Obtiene la longitud en pixels de una cadena
|
||||
int Text::lenght(const std::string &text, int kerning) {
|
||||
auto Text::lenght(const std::string &text, int kerning) -> int {
|
||||
int shift = 0;
|
||||
|
||||
for (int i = 0; i < (int)text.length(); ++i)
|
||||
shift += (offset[int(text[i])].w + kerning);
|
||||
for (int i = 0; i < (int)text.length(); ++i) {
|
||||
shift += (offset[static_cast<unsigned char>(text[i])].w + kerning);
|
||||
}
|
||||
|
||||
// Descuenta el kerning del último caracter
|
||||
return shift - kerning;
|
||||
}
|
||||
|
||||
// Devuelve el valor de la variable
|
||||
int Text::getCharacterSize() {
|
||||
auto Text::getCharacterSize() const -> int {
|
||||
return boxWidth;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ struct textFile_t {
|
||||
};
|
||||
|
||||
// Llena una estructuta textFile_t desde un fichero
|
||||
textFile_t LoadTextFile(const std::string &file, bool verbose = false);
|
||||
auto LoadTextFile(const std::string &file, bool verbose = false) -> textFile_t;
|
||||
|
||||
// Llena una estructura textFile_t desde bytes en memoria
|
||||
textFile_t LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose = false);
|
||||
auto LoadTextFileFromMemory(const std::vector<uint8_t> &bytes, bool verbose = false) -> textFile_t;
|
||||
|
||||
// Clase texto. Pinta texto en pantalla a partir de un bitmap
|
||||
class Text {
|
||||
@@ -60,7 +60,7 @@ class Text {
|
||||
|
||||
// No copiable (gestiona memoria dinámica)
|
||||
Text(const Text &) = delete;
|
||||
Text &operator=(const Text &) = delete;
|
||||
auto operator=(const Text &) -> Text & = delete;
|
||||
|
||||
// Escribe el texto en pantalla
|
||||
void write(int x, int y, const std::string &text, int kerning = 1, int lenght = -1);
|
||||
@@ -78,10 +78,10 @@ class Text {
|
||||
void writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning = 1, color_t textColor = color_t(255, 255, 255), Uint8 shadowDistance = 1, color_t shadowColor = color_t(0, 0, 0), int lenght = -1);
|
||||
|
||||
// Obtiene la longitud en pixels de una cadena
|
||||
int lenght(const std::string &text, int kerning = 1);
|
||||
auto lenght(const std::string &text, int kerning = 1) -> int;
|
||||
|
||||
// Devuelve el valor de la variable
|
||||
int getCharacterSize();
|
||||
[[nodiscard]] auto getCharacterSize() const -> int;
|
||||
|
||||
// Recarga la textura
|
||||
void reLoadTexture();
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
#include "core/rendering/texture.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdlib.h> // for exit
|
||||
|
||||
#include <cstdlib> // for exit
|
||||
#include <iostream> // for basic_ostream, operator<<, cout, endl
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "external/stb_image.h" // for stbi_failure_reason, stbi_image_free
|
||||
#include "core/resources/resource_helper.h" // for loadFile (pack + filesystem fallback)
|
||||
#include "external/stb_image.h" // for stbi_failure_reason, stbi_image_free
|
||||
|
||||
SDL_ScaleMode Texture::currentScaleMode = SDL_SCALEMODE_NEAREST;
|
||||
|
||||
@@ -32,8 +33,7 @@ Texture::Texture(SDL_Renderer *renderer, const std::vector<uint8_t> &bytes, bool
|
||||
: texture(nullptr),
|
||||
renderer(renderer),
|
||||
width(0),
|
||||
height(0),
|
||||
path("") {
|
||||
height(0) {
|
||||
if (!bytes.empty()) {
|
||||
loadFromMemory(bytes.data(), bytes.size(), renderer, verbose);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ Texture::~Texture() {
|
||||
}
|
||||
|
||||
// Helper: convierte píxeles RGBA decodificados por stbi en SDL_Texture
|
||||
static SDL_Texture *createTextureFromPixels(SDL_Renderer *renderer, unsigned char *data, int w, int h, int *out_w, int *out_h) {
|
||||
static auto createTextureFromPixels(SDL_Renderer *renderer, unsigned char *data, int w, int h, int *out_w, int *out_h) -> SDL_Texture * {
|
||||
const int pitch = 4 * w;
|
||||
SDL_Surface *loadedSurface = SDL_CreateSurfaceFrom(w, h, SDL_PIXELFORMAT_RGBA32, static_cast<void *>(data), pitch);
|
||||
if (loadedSurface == nullptr) {
|
||||
@@ -62,23 +62,32 @@ static SDL_Texture *createTextureFromPixels(SDL_Renderer *renderer, unsigned cha
|
||||
return newTexture;
|
||||
}
|
||||
|
||||
// Carga una imagen desde un fichero
|
||||
bool Texture::loadFromFile(const std::string &path, SDL_Renderer *renderer, bool verbose) {
|
||||
// Carga una imagen desde un fichero (vía ResourceHelper: pack si està inicialitzat, filesystem si no)
|
||||
auto Texture::loadFromFile(const std::string &path, SDL_Renderer *renderer, bool verbose) -> bool {
|
||||
const std::string filename = path.substr(path.find_last_of("\\/") + 1);
|
||||
|
||||
auto bytes = ResourceHelper::loadFile(path);
|
||||
if (bytes.empty()) {
|
||||
SDL_Log("Loading image failed: can't open %s", path.c_str());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int req_format = STBI_rgb_alpha;
|
||||
int w, h, orig_format;
|
||||
unsigned char *data = stbi_load(path.c_str(), &w, &h, &orig_format, req_format);
|
||||
int w;
|
||||
int h;
|
||||
int orig_format;
|
||||
unsigned char *data = stbi_load_from_memory(bytes.data(), static_cast<int>(bytes.size()), &w, &h, &orig_format, req_format);
|
||||
if (data == nullptr) {
|
||||
SDL_Log("Loading image failed: %s", stbi_failure_reason());
|
||||
exit(1);
|
||||
} else if (verbose) {
|
||||
std::cout << "Image loaded: " << filename.c_str() << std::endl;
|
||||
std::cout << "Image loaded: " << filename.c_str() << '\n';
|
||||
}
|
||||
|
||||
unload();
|
||||
SDL_Texture *newTexture = createTextureFromPixels(renderer, data, w, h, &this->width, &this->height);
|
||||
if (newTexture == nullptr && verbose) {
|
||||
std::cout << "Unable to load image " << path.c_str() << std::endl;
|
||||
std::cout << "Unable to load image " << path.c_str() << '\n';
|
||||
}
|
||||
|
||||
stbi_image_free(data);
|
||||
@@ -87,8 +96,10 @@ bool Texture::loadFromFile(const std::string &path, SDL_Renderer *renderer, bool
|
||||
}
|
||||
|
||||
// Carga una imagen desde bytes en memoria
|
||||
bool Texture::loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *renderer, bool verbose) {
|
||||
int w, h, orig_format;
|
||||
auto Texture::loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *renderer, bool verbose) -> bool {
|
||||
int w;
|
||||
int h;
|
||||
int orig_format;
|
||||
unsigned char *pixels = stbi_load_from_memory(data, (int)size, &w, &h, &orig_format, STBI_rgb_alpha);
|
||||
if (pixels == nullptr) {
|
||||
SDL_Log("Loading image from memory failed: %s", stbi_failure_reason());
|
||||
@@ -98,7 +109,7 @@ bool Texture::loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *ren
|
||||
unload();
|
||||
SDL_Texture *newTexture = createTextureFromPixels(renderer, pixels, w, h, &this->width, &this->height);
|
||||
if (newTexture == nullptr && verbose) {
|
||||
std::cout << "Unable to create texture from memory" << std::endl;
|
||||
std::cout << "Unable to create texture from memory" << '\n';
|
||||
}
|
||||
|
||||
stbi_image_free(pixels);
|
||||
@@ -107,11 +118,11 @@ bool Texture::loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *ren
|
||||
}
|
||||
|
||||
// Crea una textura en blanco
|
||||
bool Texture::createBlank(SDL_Renderer *renderer, int width, int height, SDL_TextureAccess access) {
|
||||
auto Texture::createBlank(SDL_Renderer *renderer, int width, int height, SDL_TextureAccess access) -> bool {
|
||||
// Crea una textura sin inicializar
|
||||
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, access, width, height);
|
||||
if (texture == nullptr) {
|
||||
std::cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << std::endl;
|
||||
std::cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << '\n';
|
||||
} else {
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
@@ -165,7 +176,7 @@ void Texture::render(SDL_Renderer *renderer, int x, int y, SDL_Rect *clip, float
|
||||
SDL_FRect srcRect;
|
||||
SDL_FRect *srcRectPtr = nullptr;
|
||||
if (clip != nullptr) {
|
||||
srcRect = {(float)clip->x, (float)clip->y, (float)clip->w, (float)clip->h};
|
||||
srcRect = {.x = (float)clip->x, .y = (float)clip->y, .w = (float)clip->w, .h = (float)clip->h};
|
||||
srcRectPtr = &srcRect;
|
||||
}
|
||||
|
||||
@@ -173,7 +184,7 @@ void Texture::render(SDL_Renderer *renderer, int x, int y, SDL_Rect *clip, float
|
||||
SDL_FPoint fCenter;
|
||||
SDL_FPoint *fCenterPtr = nullptr;
|
||||
if (center != nullptr) {
|
||||
fCenter = {(float)center->x, (float)center->y};
|
||||
fCenter = {.x = (float)center->x, .y = (float)center->y};
|
||||
fCenterPtr = &fCenter;
|
||||
}
|
||||
|
||||
@@ -187,21 +198,21 @@ void Texture::setAsRenderTarget(SDL_Renderer *renderer) {
|
||||
}
|
||||
|
||||
// Obtiene el ancho de la imagen
|
||||
int Texture::getWidth() {
|
||||
auto Texture::getWidth() const -> int {
|
||||
return width;
|
||||
}
|
||||
|
||||
// Obtiene el alto de la imagen
|
||||
int Texture::getHeight() {
|
||||
auto Texture::getHeight() const -> int {
|
||||
return height;
|
||||
}
|
||||
|
||||
// Recarga la textura
|
||||
bool Texture::reLoad() {
|
||||
auto Texture::reLoad() -> bool {
|
||||
return loadFromFile(path, renderer);
|
||||
}
|
||||
|
||||
// Obtiene la textura
|
||||
SDL_Texture *Texture::getSDLTexture() {
|
||||
auto Texture::getSDLTexture() -> SDL_Texture * {
|
||||
return texture;
|
||||
}
|
||||
@@ -33,13 +33,13 @@ class Texture {
|
||||
~Texture();
|
||||
|
||||
// Carga una imagen desde un fichero
|
||||
bool loadFromFile(const std::string &path, SDL_Renderer *renderer, bool verbose = false);
|
||||
auto loadFromFile(const std::string &path, SDL_Renderer *renderer, bool verbose = false) -> bool;
|
||||
|
||||
// Carga una imagen desde bytes en memoria
|
||||
bool loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *renderer, bool verbose = false);
|
||||
auto loadFromMemory(const uint8_t *data, size_t size, SDL_Renderer *renderer, bool verbose = false) -> bool;
|
||||
|
||||
// Crea una textura en blanco
|
||||
bool createBlank(SDL_Renderer *renderer, int width, int height, SDL_TextureAccess = SDL_TEXTUREACCESS_STREAMING);
|
||||
auto createBlank(SDL_Renderer *renderer, int width, int height, SDL_TextureAccess /*access*/ = SDL_TEXTUREACCESS_STREAMING) -> bool;
|
||||
|
||||
// Libera la memoria de la textura
|
||||
void unload();
|
||||
@@ -60,14 +60,14 @@ class Texture {
|
||||
void setAsRenderTarget(SDL_Renderer *renderer);
|
||||
|
||||
// Obtiene el ancho de la imagen
|
||||
int getWidth();
|
||||
[[nodiscard]] auto getWidth() const -> int;
|
||||
|
||||
// Obtiene el alto de la imagen
|
||||
int getHeight();
|
||||
[[nodiscard]] auto getHeight() const -> int;
|
||||
|
||||
// Recarga la textura
|
||||
bool reLoad();
|
||||
auto reLoad() -> bool;
|
||||
|
||||
// Obtiene la textura
|
||||
SDL_Texture *getSDLTexture();
|
||||
auto getSDLTexture() -> SDL_Texture *;
|
||||
};
|
||||
|
||||
@@ -4,19 +4,7 @@
|
||||
|
||||
// Constructor
|
||||
Writer::Writer(Text *text)
|
||||
: text(text),
|
||||
posX(0),
|
||||
posY(0),
|
||||
kerning(0),
|
||||
caption(""),
|
||||
speed(0),
|
||||
writingCounter(0),
|
||||
index(0),
|
||||
lenght(0),
|
||||
completed(false),
|
||||
enabled(false),
|
||||
enabledCounter(0),
|
||||
finished(false) {
|
||||
: text(text) {
|
||||
}
|
||||
|
||||
// Actualiza el objeto
|
||||
@@ -87,7 +75,7 @@ void Writer::setEnabled(bool value) {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool Writer::IsEnabled() {
|
||||
auto Writer::IsEnabled() const -> bool {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@@ -97,7 +85,7 @@ void Writer::setEnabledCounter(int time) {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int Writer::getEnabledCounter() {
|
||||
auto Writer::getEnabledCounter() const -> int {
|
||||
return enabledCounter;
|
||||
}
|
||||
|
||||
@@ -107,6 +95,6 @@ void Writer::center(int x) {
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool Writer::hasFinished() {
|
||||
auto Writer::hasFinished() const -> bool {
|
||||
return finished;
|
||||
}
|
||||
@@ -10,18 +10,18 @@ class Writer {
|
||||
Text *text; // Objeto encargado de escribir el texto
|
||||
|
||||
// Variables
|
||||
int posX; // Posicion en el eje X donde empezar a escribir el texto
|
||||
int posY; // Posicion en el eje Y donde empezar a escribir el texto
|
||||
int kerning; // Kerning del texto, es decir, espaciado entre caracteres
|
||||
int posX{0}; // Posicion en el eje X donde empezar a escribir el texto
|
||||
int posY{0}; // Posicion en el eje Y donde empezar a escribir el texto
|
||||
int kerning{0}; // Kerning del texto, es decir, espaciado entre caracteres
|
||||
std::string caption; // El texto para escribir
|
||||
int speed; // Velocidad de escritura
|
||||
int writingCounter; // Temporizador de escritura para cada caracter
|
||||
int index; // Posición del texto que se está escribiendo
|
||||
int lenght; // Longitud de la cadena a escribir
|
||||
bool completed; // Indica si se ha escrito todo el texto
|
||||
bool enabled; // Indica si el objeto está habilitado
|
||||
int enabledCounter; // Temporizador para deshabilitar el objeto
|
||||
bool finished; // Indica si ya ha terminado
|
||||
int speed{0}; // Velocidad de escritura
|
||||
int writingCounter{0}; // Temporizador de escritura para cada caracter
|
||||
int index{0}; // Posición del texto que se está escribiendo
|
||||
int lenght{0}; // Longitud de la cadena a escribir
|
||||
bool completed{false}; // Indica si se ha escrito todo el texto
|
||||
bool enabled{false}; // Indica si el objeto está habilitado
|
||||
int enabledCounter{0}; // Temporizador para deshabilitar el objeto
|
||||
bool finished{false}; // Indica si ya ha terminado
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
@@ -52,17 +52,17 @@ class Writer {
|
||||
void setEnabled(bool value);
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool IsEnabled();
|
||||
[[nodiscard]] auto IsEnabled() const -> bool;
|
||||
|
||||
// Establece el valor de la variable
|
||||
void setEnabledCounter(int time);
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int getEnabledCounter();
|
||||
[[nodiscard]] auto getEnabledCounter() const -> int;
|
||||
|
||||
// Centra la cadena de texto a un punto X
|
||||
void center(int x);
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
bool hasFinished();
|
||||
[[nodiscard]] auto hasFinished() const -> bool;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "core/resources/asset.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stddef.h> // for size_t
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <iostream> // for basic_ostream, operator<<, cout, endl
|
||||
|
||||
#include "core/resources/resource_helper.h"
|
||||
@@ -26,9 +26,7 @@ auto Asset::get() -> Asset * {
|
||||
|
||||
// Constructor
|
||||
Asset::Asset(const std::string &executablePath)
|
||||
: longestName(0),
|
||||
executablePath(executablePath.substr(0, executablePath.find_last_of("\\/"))),
|
||||
verbose(true) {
|
||||
: executablePath(executablePath.substr(0, executablePath.find_last_of("\\/"))) {
|
||||
}
|
||||
|
||||
// Añade un elemento a la lista
|
||||
@@ -44,10 +42,10 @@ void Asset::add(const std::string &file, enum assetType type, bool required, boo
|
||||
}
|
||||
|
||||
// Devuelve el fichero de un elemento de la lista a partir de una cadena
|
||||
std::string Asset::get(const std::string &text) {
|
||||
auto Asset::get(const std::string &text) -> std::string {
|
||||
for (const auto &f : fileList) {
|
||||
const size_t lastIndex = f.file.find_last_of("/") + 1;
|
||||
const std::string file = f.file.substr(lastIndex, std::string::npos);
|
||||
const size_t lastIndex = f.file.find_last_of('/') + 1;
|
||||
const std::string file = f.file.substr(lastIndex);
|
||||
|
||||
if (file == text) {
|
||||
return f.file;
|
||||
@@ -55,20 +53,20 @@ std::string Asset::get(const std::string &text) {
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Warning: file " << text.c_str() << " not found" << std::endl;
|
||||
std::cout << "Warning: file " << text.c_str() << " not found" << '\n';
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Comprueba que existen todos los elementos
|
||||
bool Asset::check() {
|
||||
auto Asset::check() -> bool {
|
||||
bool success = true;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "\n** Checking files" << std::endl;
|
||||
std::cout << "\n** Checking files" << '\n';
|
||||
|
||||
std::cout << "Executable path is: " << executablePath << std::endl;
|
||||
std::cout << "Sample filepath: " << fileList.back().file << std::endl;
|
||||
std::cout << "Executable path is: " << executablePath << '\n';
|
||||
std::cout << "Sample filepath: " << fileList.back().file << '\n';
|
||||
}
|
||||
|
||||
// Comprueba la lista de ficheros clasificandolos por tipo
|
||||
@@ -85,7 +83,7 @@ bool Asset::check() {
|
||||
// Si hay ficheros de ese tipo, comprueba si existen
|
||||
if (any) {
|
||||
if (verbose) {
|
||||
std::cout << "\n>> " << getTypeName(type).c_str() << " FILES" << std::endl;
|
||||
std::cout << "\n>> " << getTypeName(type).c_str() << " FILES" << '\n';
|
||||
}
|
||||
|
||||
for (const auto &f : fileList) {
|
||||
@@ -100,10 +98,10 @@ bool Asset::check() {
|
||||
if (verbose) {
|
||||
if (success) {
|
||||
std::cout << "\n** All files OK.\n"
|
||||
<< std::endl;
|
||||
<< '\n';
|
||||
} else {
|
||||
std::cout << "\n** A file is missing. Exiting.\n"
|
||||
<< std::endl;
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +109,7 @@ bool Asset::check() {
|
||||
}
|
||||
|
||||
// Comprueba que existe un fichero
|
||||
bool Asset::checkFile(const std::string &path) {
|
||||
auto Asset::checkFile(const std::string &path) const -> bool {
|
||||
bool success = false;
|
||||
std::string result = "ERROR";
|
||||
|
||||
@@ -138,14 +136,14 @@ bool Asset::checkFile(const std::string &path) {
|
||||
std::cout.width(longestName + 2);
|
||||
std::cout.fill('.');
|
||||
std::cout << filename + " ";
|
||||
std::cout << " [" + result + "]" << std::endl;
|
||||
std::cout << " [" + result + "]" << '\n';
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Devuelve el nombre del tipo de recurso
|
||||
std::string Asset::getTypeName(int type) {
|
||||
auto Asset::getTypeName(int type) -> std::string {
|
||||
switch (type) {
|
||||
case t_bitmap:
|
||||
return "BITMAP";
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string> // for string, basic_string
|
||||
#include <vector> // for vector
|
||||
#include <cstdint> // for uint8_t
|
||||
#include <string> // for string, basic_string
|
||||
#include <vector> // for vector
|
||||
|
||||
enum assetType {
|
||||
enum assetType : std::uint8_t {
|
||||
t_bitmap,
|
||||
t_music,
|
||||
t_sound,
|
||||
@@ -28,16 +29,16 @@ class Asset {
|
||||
|
||||
private:
|
||||
// Variables
|
||||
int longestName; // Contiene la longitud del nombre de fichero mas largo
|
||||
int longestName{0}; // Contiene la longitud del nombre de fichero mas largo
|
||||
std::vector<item_t> fileList; // Listado con todas las rutas a los ficheros
|
||||
std::string executablePath; // Ruta al ejecutable
|
||||
bool verbose; // Indica si ha de mostrar información por pantalla
|
||||
bool verbose{true}; // Indica si ha de mostrar información por pantalla
|
||||
|
||||
// Comprueba que existe un fichero
|
||||
bool checkFile(const std::string &executablePath);
|
||||
[[nodiscard]] auto checkFile(const std::string &executablePath) const -> bool;
|
||||
|
||||
// Devuelve el nombre del tipo de recurso
|
||||
std::string getTypeName(int type);
|
||||
static auto getTypeName(int type) -> std::string;
|
||||
|
||||
// Constructor privado (usar Asset::init)
|
||||
explicit Asset(const std::string &path);
|
||||
@@ -55,13 +56,13 @@ class Asset {
|
||||
void add(const std::string &file, enum assetType type, bool required = true, bool absolute = false);
|
||||
|
||||
// Devuelve un elemento de la lista a partir de una cadena
|
||||
std::string get(const std::string &text);
|
||||
auto get(const std::string &text) -> std::string;
|
||||
|
||||
// Devuelve toda la lista de items registrados
|
||||
const std::vector<item_t> &getAll() const { return fileList; }
|
||||
[[nodiscard]] auto getAll() const -> const std::vector<item_t> & { return fileList; }
|
||||
|
||||
// Comprueba que existen todos los elementos
|
||||
bool check();
|
||||
auto check() -> bool;
|
||||
|
||||
// Establece si ha de mostrar texto por pantalla
|
||||
void setVerbose(bool value);
|
||||
|
||||
@@ -17,14 +17,16 @@
|
||||
|
||||
Resource *Resource::instance_ = nullptr;
|
||||
|
||||
static std::string basename(const std::string &path) {
|
||||
static auto basename(const std::string &path) -> std::string {
|
||||
return path.substr(path.find_last_of("\\/") + 1);
|
||||
}
|
||||
|
||||
static std::string stem(const std::string &path) {
|
||||
static auto stem(const std::string &path) -> std::string {
|
||||
std::string b = basename(path);
|
||||
size_t dot = b.find_last_of('.');
|
||||
if (dot == std::string::npos) return b;
|
||||
if (dot == std::string::npos) {
|
||||
return b;
|
||||
}
|
||||
return b.substr(0, dot);
|
||||
}
|
||||
|
||||
@@ -40,7 +42,7 @@ void Resource::destroy() {
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
Resource *Resource::get() {
|
||||
auto Resource::get() -> Resource * {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
@@ -48,19 +50,29 @@ Resource::Resource(SDL_Renderer *renderer)
|
||||
: renderer_(renderer) {}
|
||||
|
||||
Resource::~Resource() {
|
||||
for (auto &[name, m] : menus_) delete m;
|
||||
for (auto &[name, m] : menus_) {
|
||||
delete m;
|
||||
}
|
||||
menus_.clear();
|
||||
|
||||
for (auto &[name, t] : texts_) delete t;
|
||||
for (auto &[name, t] : texts_) {
|
||||
delete t;
|
||||
}
|
||||
texts_.clear();
|
||||
|
||||
for (auto &[name, t] : textures_) delete t;
|
||||
for (auto &[name, t] : textures_) {
|
||||
delete t;
|
||||
}
|
||||
textures_.clear();
|
||||
|
||||
for (auto &[name, s] : sounds_) JA_DeleteSound(s);
|
||||
for (auto &[name, s] : sounds_) {
|
||||
JA_DeleteSound(s);
|
||||
}
|
||||
sounds_.clear();
|
||||
|
||||
for (auto &[name, m] : musics_) JA_DeleteMusic(m);
|
||||
for (auto &[name, m] : musics_) {
|
||||
JA_DeleteMusic(m);
|
||||
}
|
||||
musics_.clear();
|
||||
}
|
||||
|
||||
@@ -74,7 +86,9 @@ void Resource::preloadAll() {
|
||||
continue;
|
||||
}
|
||||
auto bytes = ResourceHelper::loadFile(it.file);
|
||||
if (bytes.empty()) continue;
|
||||
if (bytes.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string bname = basename(it.file);
|
||||
|
||||
@@ -86,12 +100,16 @@ void Resource::preloadAll() {
|
||||
}
|
||||
case t_sound: {
|
||||
JA_Sound_t *s = JA_LoadSound(bytes.data(), (uint32_t)bytes.size());
|
||||
if (s) sounds_[bname] = s;
|
||||
if (s != nullptr) {
|
||||
sounds_[bname] = s;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case t_music: {
|
||||
JA_Music_t *m = JA_LoadMusic(bytes.data(), (Uint32)bytes.size());
|
||||
if (m) musics_[bname] = m;
|
||||
if (m != nullptr) {
|
||||
musics_[bname] = m;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case t_data: {
|
||||
@@ -103,7 +121,9 @@ void Resource::preloadAll() {
|
||||
while (std::getline(ss, line)) {
|
||||
// Normalitza CRLF perquè loadFromVector compari línies amb literals
|
||||
// ("[animation]", "[/animation]") sense \r residual.
|
||||
if (!line.empty() && line.back() == '\r') line.pop_back();
|
||||
if (!line.empty() && line.back() == '\r') {
|
||||
line.pop_back();
|
||||
}
|
||||
lines.push_back(line);
|
||||
}
|
||||
animationLines_[bname] = std::move(lines);
|
||||
@@ -114,12 +134,8 @@ void Resource::preloadAll() {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case t_font:
|
||||
// Fonts: se emparejan en pass 2
|
||||
break;
|
||||
case t_lang:
|
||||
// Lenguaje: lo sigue leyendo la clase Lang a través de ResourceHelper
|
||||
break;
|
||||
case t_font: // Fonts: se emparejan en pass 2
|
||||
case t_lang: // Lenguaje: lo sigue leyendo la clase Lang via ResourceHelper
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -131,9 +147,13 @@ void Resource::preloadAll() {
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> fontPngs;
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> fontTxts;
|
||||
for (const auto &it : items) {
|
||||
if (it.type != t_font) continue;
|
||||
if (it.type != t_font) {
|
||||
continue;
|
||||
}
|
||||
auto bytes = ResourceHelper::loadFile(it.file);
|
||||
if (bytes.empty()) continue;
|
||||
if (bytes.empty()) {
|
||||
continue;
|
||||
}
|
||||
const std::string s = stem(it.file);
|
||||
const std::string bname = basename(it.file);
|
||||
if (bname.size() >= 4 && bname.substr(bname.size() - 4) == ".png") {
|
||||
@@ -144,7 +164,9 @@ void Resource::preloadAll() {
|
||||
}
|
||||
for (const auto &[s, png] : fontPngs) {
|
||||
auto itTxt = fontTxts.find(s);
|
||||
if (itTxt == fontTxts.end()) continue;
|
||||
if (itTxt == fontTxts.end()) {
|
||||
continue;
|
||||
}
|
||||
Text *t = new Text(png, itTxt->second, renderer_);
|
||||
texts_[s] = t;
|
||||
}
|
||||
@@ -154,11 +176,17 @@ void Resource::preloadAll() {
|
||||
// requiere que Menu se adapte a cargar desde ResourceHelper. Por ahora
|
||||
// lo dejamos así y será una migración del paso 7.
|
||||
for (const auto &it : items) {
|
||||
if (it.type != t_data) continue;
|
||||
if (it.type != t_data) {
|
||||
continue;
|
||||
}
|
||||
const std::string bname = basename(it.file);
|
||||
if (bname.size() < 4 || bname.substr(bname.size() - 4) != ".men") continue;
|
||||
if (bname.size() < 4 || bname.substr(bname.size() - 4) != ".men") {
|
||||
continue;
|
||||
}
|
||||
auto bytes = ResourceHelper::loadFile(it.file);
|
||||
if (bytes.empty()) continue;
|
||||
if (bytes.empty()) {
|
||||
continue;
|
||||
}
|
||||
Menu *m = new Menu(renderer_, "");
|
||||
m->loadFromBytes(bytes, bname);
|
||||
const std::string s = stem(it.file);
|
||||
@@ -166,7 +194,7 @@ void Resource::preloadAll() {
|
||||
}
|
||||
}
|
||||
|
||||
Texture *Resource::getTexture(const std::string &name) {
|
||||
auto Resource::getTexture(const std::string &name) -> Texture * {
|
||||
auto it = textures_.find(name);
|
||||
if (it == textures_.end()) {
|
||||
std::cerr << "Resource::getTexture: missing " << name << '\n';
|
||||
@@ -175,7 +203,7 @@ Texture *Resource::getTexture(const std::string &name) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
JA_Sound_t *Resource::getSound(const std::string &name) {
|
||||
auto Resource::getSound(const std::string &name) -> JA_Sound_t * {
|
||||
auto it = sounds_.find(name);
|
||||
if (it == sounds_.end()) {
|
||||
std::cerr << "Resource::getSound: missing " << name << '\n';
|
||||
@@ -184,7 +212,7 @@ JA_Sound_t *Resource::getSound(const std::string &name) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
JA_Music_t *Resource::getMusic(const std::string &name) {
|
||||
auto Resource::getMusic(const std::string &name) -> JA_Music_t * {
|
||||
auto it = musics_.find(name);
|
||||
if (it == musics_.end()) {
|
||||
std::cerr << "Resource::getMusic: missing " << name << '\n';
|
||||
@@ -193,7 +221,7 @@ JA_Music_t *Resource::getMusic(const std::string &name) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
std::vector<std::string> &Resource::getAnimationLines(const std::string &name) {
|
||||
auto Resource::getAnimationLines(const std::string &name) -> std::vector<std::string> & {
|
||||
auto it = animationLines_.find(name);
|
||||
if (it == animationLines_.end()) {
|
||||
static std::vector<std::string> empty;
|
||||
@@ -203,7 +231,7 @@ std::vector<std::string> &Resource::getAnimationLines(const std::string &name) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Text *Resource::getText(const std::string &name) {
|
||||
auto Resource::getText(const std::string &name) -> Text * {
|
||||
auto it = texts_.find(name);
|
||||
if (it == texts_.end()) {
|
||||
std::cerr << "Resource::getText: missing " << name << '\n';
|
||||
@@ -212,7 +240,7 @@ Text *Resource::getText(const std::string &name) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Menu *Resource::getMenu(const std::string &name) {
|
||||
auto Resource::getMenu(const std::string &name) -> Menu * {
|
||||
auto it = menus_.find(name);
|
||||
if (it == menus_.end()) {
|
||||
std::cerr << "Resource::getMenu: missing " << name << '\n';
|
||||
|
||||
@@ -19,15 +19,15 @@ class Resource {
|
||||
public:
|
||||
static void init(SDL_Renderer *renderer);
|
||||
static void destroy();
|
||||
static Resource *get();
|
||||
static auto get() -> Resource *;
|
||||
|
||||
Texture *getTexture(const std::string &name);
|
||||
JA_Sound_t *getSound(const std::string &name);
|
||||
JA_Music_t *getMusic(const std::string &name);
|
||||
std::vector<std::string> &getAnimationLines(const std::string &name);
|
||||
Text *getText(const std::string &name); // name sin extensión: "smb2", "nokia2", ...
|
||||
Menu *getMenu(const std::string &name); // name sin extensión: "title", "options", ...
|
||||
const std::vector<uint8_t> &getDemoBytes() const { return demoBytes_; }
|
||||
auto getTexture(const std::string &name) -> Texture *;
|
||||
auto getSound(const std::string &name) -> JA_Sound_t *;
|
||||
auto getMusic(const std::string &name) -> JA_Music_t *;
|
||||
auto getAnimationLines(const std::string &name) -> std::vector<std::string> &;
|
||||
auto getText(const std::string &name) -> Text *; // name sin extensión: "smb2", "nokia2", ...
|
||||
auto getMenu(const std::string &name) -> Menu *; // name sin extensión: "title", "options", ...
|
||||
auto getDemoBytes() const -> const std::vector<uint8_t> & { return demoBytes_; }
|
||||
|
||||
private:
|
||||
explicit Resource(SDL_Renderer *renderer);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace ResourceHelper {
|
||||
static bool resource_system_initialized = false;
|
||||
|
||||
bool initializeResourceSystem(const std::string& pack_file, bool enable_fallback) {
|
||||
auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool {
|
||||
auto& loader = ResourceLoader::getInstance();
|
||||
bool ok = loader.initialize(pack_file, enable_fallback);
|
||||
resource_system_initialized = ok;
|
||||
@@ -31,7 +31,7 @@ namespace ResourceHelper {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> loadFile(const std::string& filepath) {
|
||||
auto loadFile(const std::string& filepath) -> std::vector<uint8_t> {
|
||||
if (resource_system_initialized && shouldUseResourcePack(filepath)) {
|
||||
auto& loader = ResourceLoader::getInstance();
|
||||
std::string pack_path = getPackPath(filepath);
|
||||
@@ -58,14 +58,14 @@ namespace ResourceHelper {
|
||||
return data;
|
||||
}
|
||||
|
||||
bool shouldUseResourcePack(const std::string& filepath) {
|
||||
auto shouldUseResourcePack(const std::string& filepath) -> bool {
|
||||
// Solo entran al pack los ficheros dentro de data/
|
||||
return filepath.find("data/") != std::string::npos;
|
||||
}
|
||||
|
||||
std::string getPackPath(const std::string& asset_path) {
|
||||
auto getPackPath(const std::string& asset_path) -> std::string {
|
||||
std::string pack_path = asset_path;
|
||||
std::replace(pack_path.begin(), pack_path.end(), '\\', '/');
|
||||
std::ranges::replace(pack_path, '\\', '/');
|
||||
|
||||
// Toma la última aparición de "data/" como prefijo a quitar
|
||||
size_t last_data = pack_path.rfind("data/");
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
#include <vector>
|
||||
|
||||
namespace ResourceHelper {
|
||||
bool initializeResourceSystem(const std::string& pack_file = "resources.pack", bool enable_fallback = true);
|
||||
auto initializeResourceSystem(const std::string& pack_file = "resources.pack", bool enable_fallback = true) -> bool;
|
||||
void shutdownResourceSystem();
|
||||
|
||||
std::vector<uint8_t> loadFile(const std::string& filepath);
|
||||
auto loadFile(const std::string& filepath) -> std::vector<uint8_t>;
|
||||
|
||||
bool shouldUseResourcePack(const std::string& filepath);
|
||||
std::string getPackPath(const std::string& asset_path);
|
||||
auto shouldUseResourcePack(const std::string& filepath) -> bool;
|
||||
auto getPackPath(const std::string& asset_path) -> std::string;
|
||||
} // namespace ResourceHelper
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
|
||||
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
||||
|
||||
ResourceLoader::ResourceLoader()
|
||||
: resource_pack_(nullptr),
|
||||
fallback_to_files_(true) {}
|
||||
ResourceLoader::ResourceLoader() = default;
|
||||
|
||||
ResourceLoader& ResourceLoader::getInstance() {
|
||||
auto ResourceLoader::getInstance() -> ResourceLoader& {
|
||||
if (!instance) {
|
||||
instance = std::unique_ptr<ResourceLoader>(new ResourceLoader());
|
||||
}
|
||||
@@ -24,7 +22,7 @@ ResourceLoader::~ResourceLoader() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
bool ResourceLoader::initialize(const std::string& pack_file, bool enable_fallback) {
|
||||
auto ResourceLoader::initialize(const std::string& pack_file, bool enable_fallback) -> bool {
|
||||
shutdown();
|
||||
|
||||
fallback_to_files_ = enable_fallback;
|
||||
@@ -55,7 +53,7 @@ void ResourceLoader::shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ResourceLoader::loadResource(const std::string& filename) {
|
||||
auto ResourceLoader::loadResource(const std::string& filename) -> std::vector<uint8_t> {
|
||||
if ((resource_pack_ != nullptr) && resource_pack_->hasResource(filename)) {
|
||||
return resource_pack_->getResource(filename);
|
||||
}
|
||||
@@ -68,7 +66,7 @@ std::vector<uint8_t> ResourceLoader::loadResource(const std::string& filename) {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ResourceLoader::resourceExists(const std::string& filename) {
|
||||
auto ResourceLoader::resourceExists(const std::string& filename) -> bool {
|
||||
if ((resource_pack_ != nullptr) && resource_pack_->hasResource(filename)) {
|
||||
return true;
|
||||
}
|
||||
@@ -81,7 +79,7 @@ bool ResourceLoader::resourceExists(const std::string& filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ResourceLoader::loadFromFile(const std::string& filename) {
|
||||
auto ResourceLoader::loadFromFile(const std::string& filename) -> std::vector<uint8_t> {
|
||||
std::string full_path = getDataPath(filename);
|
||||
|
||||
std::ifstream file(full_path, std::ios::binary | std::ios::ate);
|
||||
@@ -102,18 +100,18 @@ std::vector<uint8_t> ResourceLoader::loadFromFile(const std::string& filename) {
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string ResourceLoader::getDataPath(const std::string& filename) {
|
||||
auto ResourceLoader::getDataPath(const std::string& filename) -> std::string {
|
||||
return "data/" + filename;
|
||||
}
|
||||
|
||||
size_t ResourceLoader::getLoadedResourceCount() const {
|
||||
auto ResourceLoader::getLoadedResourceCount() const -> size_t {
|
||||
if (resource_pack_ != nullptr) {
|
||||
return resource_pack_->getResourceCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> ResourceLoader::getAvailableResources() const {
|
||||
auto ResourceLoader::getAvailableResources() const -> std::vector<std::string> {
|
||||
if (resource_pack_ != nullptr) {
|
||||
return resource_pack_->getResourceList();
|
||||
}
|
||||
@@ -123,7 +121,7 @@ std::vector<std::string> ResourceLoader::getAvailableResources() const {
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator("data")) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::string filename = std::filesystem::relative(entry.path(), "data").string();
|
||||
std::replace(filename.begin(), filename.end(), '\\', '/');
|
||||
std::ranges::replace(filename, '\\', '/');
|
||||
result.push_back(filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,29 +11,29 @@ class ResourcePack;
|
||||
class ResourceLoader {
|
||||
private:
|
||||
static std::unique_ptr<ResourceLoader> instance;
|
||||
ResourcePack* resource_pack_;
|
||||
ResourcePack* resource_pack_{nullptr};
|
||||
std::string pack_path_;
|
||||
bool fallback_to_files_;
|
||||
bool fallback_to_files_{true};
|
||||
|
||||
ResourceLoader();
|
||||
|
||||
public:
|
||||
static ResourceLoader& getInstance();
|
||||
static auto getInstance() -> ResourceLoader&;
|
||||
~ResourceLoader();
|
||||
|
||||
bool initialize(const std::string& pack_file, bool enable_fallback = true);
|
||||
auto initialize(const std::string& pack_file, bool enable_fallback = true) -> bool;
|
||||
void shutdown();
|
||||
|
||||
std::vector<uint8_t> loadResource(const std::string& filename);
|
||||
bool resourceExists(const std::string& filename);
|
||||
auto loadResource(const std::string& filename) -> std::vector<uint8_t>;
|
||||
auto resourceExists(const std::string& filename) -> bool;
|
||||
|
||||
void setFallbackToFiles(bool enable) { fallback_to_files_ = enable; }
|
||||
bool getFallbackToFiles() const { return fallback_to_files_; }
|
||||
[[nodiscard]] auto getFallbackToFiles() const -> bool { return fallback_to_files_; }
|
||||
|
||||
size_t getLoadedResourceCount() const;
|
||||
std::vector<std::string> getAvailableResources() const;
|
||||
[[nodiscard]] auto getLoadedResourceCount() const -> size_t;
|
||||
[[nodiscard]] auto getAvailableResources() const -> std::vector<std::string>;
|
||||
|
||||
private:
|
||||
static std::vector<uint8_t> loadFromFile(const std::string& filename);
|
||||
static std::string getDataPath(const std::string& filename);
|
||||
static auto loadFromFile(const std::string& filename) -> std::vector<uint8_t>;
|
||||
static auto getDataPath(const std::string& filename) -> std::string;
|
||||
};
|
||||
|
||||
@@ -10,19 +10,20 @@
|
||||
|
||||
const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCRS_RESOURCES__2026";
|
||||
|
||||
ResourcePack::ResourcePack()
|
||||
: loaded_(false) {}
|
||||
ResourcePack::ResourcePack() = default;
|
||||
|
||||
ResourcePack::~ResourcePack() {
|
||||
clear();
|
||||
}
|
||||
|
||||
uint32_t ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) {
|
||||
auto ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t {
|
||||
return std::accumulate(data.begin(), data.end(), uint32_t(0x12345678), [](uint32_t acc, uint8_t b) { return ((acc << 5) + acc) + b; });
|
||||
}
|
||||
|
||||
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] ^= key[i % key.length()];
|
||||
}
|
||||
@@ -32,7 +33,7 @@ void ResourcePack::decryptData(std::vector<uint8_t>& data, const std::string& ke
|
||||
encryptData(data, key);
|
||||
}
|
||||
|
||||
bool ResourcePack::loadPack(const std::string& pack_file) {
|
||||
auto ResourcePack::loadPack(const std::string& pack_file) -> bool {
|
||||
std::ifstream file(pack_file, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open pack file: " << pack_file << '\n';
|
||||
@@ -87,7 +88,7 @@ bool ResourcePack::loadPack(const std::string& pack_file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourcePack::savePack(const std::string& pack_file) {
|
||||
auto ResourcePack::savePack(const std::string& pack_file) -> bool {
|
||||
std::ofstream file(pack_file, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not create pack file: " << pack_file << '\n';
|
||||
@@ -99,11 +100,11 @@ bool ResourcePack::savePack(const std::string& pack_file) {
|
||||
uint32_t version = 1;
|
||||
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
||||
|
||||
uint32_t resource_count = static_cast<uint32_t>(resources_.size());
|
||||
auto resource_count = static_cast<uint32_t>(resources_.size());
|
||||
file.write(reinterpret_cast<const char*>(&resource_count), sizeof(resource_count));
|
||||
|
||||
for (const auto& [filename, entry] : resources_) {
|
||||
uint32_t filename_length = static_cast<uint32_t>(filename.length());
|
||||
auto filename_length = static_cast<uint32_t>(filename.length());
|
||||
file.write(reinterpret_cast<const char*>(&filename_length), sizeof(filename_length));
|
||||
file.write(filename.c_str(), filename_length);
|
||||
|
||||
@@ -122,7 +123,7 @@ bool ResourcePack::savePack(const std::string& pack_file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourcePack::addFile(const std::string& filename, const std::string& filepath) {
|
||||
auto ResourcePack::addFile(const std::string& filename, const std::string& filepath) -> bool {
|
||||
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open file: " << filepath << '\n';
|
||||
@@ -150,7 +151,7 @@ bool ResourcePack::addFile(const std::string& filename, const std::string& filep
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResourcePack::addDirectory(const std::string& directory) {
|
||||
auto ResourcePack::addDirectory(const std::string& directory) -> bool {
|
||||
if (!std::filesystem::exists(directory)) {
|
||||
std::cerr << "Error: Directory does not exist: " << directory << '\n';
|
||||
return false;
|
||||
@@ -162,7 +163,7 @@ bool ResourcePack::addDirectory(const std::string& directory) {
|
||||
std::string filepath = entry.path().string();
|
||||
std::string filename = std::filesystem::relative(entry.path(), directory).string();
|
||||
|
||||
std::replace(filename.begin(), filename.end(), '\\', '/');
|
||||
std::ranges::replace(filename, '\\', '/');
|
||||
|
||||
if (!addFile(filename, filepath)) {
|
||||
return false;
|
||||
@@ -173,7 +174,7 @@ bool ResourcePack::addDirectory(const std::string& directory) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ResourcePack::getResource(const std::string& filename) {
|
||||
auto ResourcePack::getResource(const std::string& filename) -> std::vector<uint8_t> {
|
||||
auto it = resources_.find(filename);
|
||||
if (it == resources_.end()) {
|
||||
std::cerr << "Error: Resource not found: " << filename << '\n';
|
||||
@@ -197,7 +198,7 @@ std::vector<uint8_t> ResourcePack::getResource(const std::string& filename) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ResourcePack::hasResource(const std::string& filename) const {
|
||||
auto ResourcePack::hasResource(const std::string& filename) const -> bool {
|
||||
return resources_.find(filename) != resources_.end();
|
||||
}
|
||||
|
||||
@@ -207,11 +208,11 @@ void ResourcePack::clear() {
|
||||
loaded_ = false;
|
||||
}
|
||||
|
||||
size_t ResourcePack::getResourceCount() const {
|
||||
auto ResourcePack::getResourceCount() const -> size_t {
|
||||
return resources_.size();
|
||||
}
|
||||
|
||||
std::vector<std::string> ResourcePack::getResourceList() const {
|
||||
auto ResourcePack::getResourceList() const -> std::vector<std::string> {
|
||||
std::vector<std::string> result;
|
||||
result.reserve(resources_.size());
|
||||
for (const auto& [filename, entry] : resources_) {
|
||||
|
||||
@@ -17,9 +17,9 @@ class ResourcePack {
|
||||
private:
|
||||
std::unordered_map<std::string, ResourceEntry> resources_;
|
||||
std::vector<uint8_t> data_;
|
||||
bool loaded_;
|
||||
bool loaded_{false};
|
||||
|
||||
static uint32_t calculateChecksum(const std::vector<uint8_t>& data);
|
||||
static auto calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t;
|
||||
static void encryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||
static void decryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||
|
||||
@@ -27,18 +27,18 @@ class ResourcePack {
|
||||
ResourcePack();
|
||||
~ResourcePack();
|
||||
|
||||
bool loadPack(const std::string& pack_file);
|
||||
bool savePack(const std::string& pack_file);
|
||||
auto loadPack(const std::string& pack_file) -> bool;
|
||||
auto savePack(const std::string& pack_file) -> bool;
|
||||
|
||||
bool addFile(const std::string& filename, const std::string& filepath);
|
||||
bool addDirectory(const std::string& directory);
|
||||
auto addFile(const std::string& filename, const std::string& filepath) -> bool;
|
||||
auto addDirectory(const std::string& directory) -> bool;
|
||||
|
||||
std::vector<uint8_t> getResource(const std::string& filename);
|
||||
bool hasResource(const std::string& filename) const;
|
||||
auto getResource(const std::string& filename) -> std::vector<uint8_t>;
|
||||
auto hasResource(const std::string& filename) const -> bool;
|
||||
|
||||
void clear();
|
||||
size_t getResourceCount() const;
|
||||
std::vector<std::string> getResourceList() const;
|
||||
auto getResourceCount() const -> size_t;
|
||||
auto getResourceList() const -> std::vector<std::string>;
|
||||
|
||||
static const std::string DEFAULT_ENCRYPT_KEY;
|
||||
};
|
||||
|
||||
+11
-11
@@ -96,16 +96,16 @@ class Menu {
|
||||
std::string font_txt;
|
||||
|
||||
// Carga la configuración del menu desde un archivo de texto
|
||||
bool load(const std::string &file_path);
|
||||
auto load(const std::string &file_path) -> bool;
|
||||
|
||||
// Parser compartido (recibe cualquier istream)
|
||||
bool parseFromStream(std::istream &file, const std::string &filename);
|
||||
auto parseFromStream(std::istream &file, const std::string &filename) -> bool;
|
||||
|
||||
// Asigna variables a partir de dos cadenas
|
||||
bool setVars(const std::string &var, const std::string &value);
|
||||
auto setVars(const std::string &var, const std::string &value) -> bool;
|
||||
|
||||
// Asigna variables a partir de dos cadenas
|
||||
bool setItem(item_t *item, const std::string &var, const std::string &value);
|
||||
auto setItem(item_t *item, const std::string &var, const std::string &value) -> bool;
|
||||
|
||||
// Actualiza el menu para recolocarlo correctamente y establecer el tamaño
|
||||
void reorganize();
|
||||
@@ -120,22 +120,22 @@ class Menu {
|
||||
void updateSelector();
|
||||
|
||||
// Obtiene la anchura del elemento más ancho del menu
|
||||
int getWidestItem();
|
||||
auto getWidestItem() -> int;
|
||||
|
||||
// Gestiona la entrada de teclado y mando durante el menu
|
||||
void checkMenuInput(Menu *menu);
|
||||
|
||||
// Calcula el ancho del menu
|
||||
int findWidth();
|
||||
auto findWidth() -> int;
|
||||
|
||||
// Calcula el alto del menu
|
||||
int findHeight();
|
||||
auto findHeight() -> int;
|
||||
|
||||
// Recoloca los elementos del menu en el eje Y
|
||||
void replaceElementsOnY();
|
||||
|
||||
// Calcula la altura del selector
|
||||
int getSelectorHeight(int value);
|
||||
auto getSelectorHeight(int value) -> int;
|
||||
|
||||
// Calcula los colores del selector para el degradado
|
||||
void setSelectorItemColors();
|
||||
@@ -148,16 +148,16 @@ class Menu {
|
||||
~Menu();
|
||||
|
||||
// Carga el menu desde bytes en memoria
|
||||
bool loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "");
|
||||
auto loadFromBytes(const std::vector<uint8_t> &bytes, const std::string &nameForLogs = "") -> bool;
|
||||
|
||||
// Carga los ficheros de audio
|
||||
void loadAudioFile(const std::string &file, int sound);
|
||||
|
||||
// Obtiene el nombre del menu
|
||||
const std::string &getName() const;
|
||||
[[nodiscard]] auto getName() const -> const std::string &;
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
int getItemSelected();
|
||||
auto getItemSelected() -> int;
|
||||
|
||||
// Deja el menu apuntando al primer elemento
|
||||
void reset();
|
||||
|
||||
Reference in New Issue
Block a user