ajustant el jugador
This commit is contained in:
@@ -30,10 +30,10 @@ class Screen {
|
||||
|
||||
// Renderizado
|
||||
void clearRenderer(Rgb color = {0x00, 0x00, 0x00}); // Limpia el renderer
|
||||
void clearSurface(Uint8 index); // Limpia la game_surface_
|
||||
void start(); // Prepara para empezar a dibujar en la textura de juego
|
||||
void render(); // Vuelca el contenido del renderizador en pantalla
|
||||
void update(float delta_time); // Actualiza la lógica de la clase
|
||||
void clearSurface(Uint8 index); // Limpia la game_surface_
|
||||
void start(); // Prepara para empezar a dibujar en la textura de juego
|
||||
void render(); // Vuelca el contenido del renderizador en pantalla
|
||||
void update(float delta_time); // Actualiza la lógica de la clase
|
||||
|
||||
// Video y ventana
|
||||
void setVideoMode(bool mode); // Establece el modo de video
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Resource {
|
||||
Cache* Cache::cache = nullptr;
|
||||
|
||||
// [SINGLETON] Crearemos el objeto cache con esta función estática
|
||||
void Cache::init() { Cache::cache = new Cache(); }
|
||||
void Cache::init(LoadingMode mode) { Cache::cache = new Cache(mode); }
|
||||
|
||||
// [SINGLETON] Destruiremos el objeto cache con esta función estática
|
||||
void Cache::destroy() { delete Cache::cache; }
|
||||
@@ -39,8 +39,8 @@ namespace Resource {
|
||||
auto Cache::get() -> Cache* { return Cache::cache; }
|
||||
|
||||
// Constructor
|
||||
Cache::Cache()
|
||||
: loading_text_(Screen::get()->getText()) {
|
||||
Cache::Cache(LoadingMode mode)
|
||||
: loading_mode_(mode), loading_text_(Screen::get()->getText()) {
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -59,18 +59,25 @@ namespace Resource {
|
||||
void Cache::load() {
|
||||
// Nota: el overlay de debug (RenderInfo) se inicializa después de esta carga,
|
||||
// por lo que updateZoomFactor() se llamará correctamente en RenderInfo::init().
|
||||
calculateTotal();
|
||||
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
|
||||
std::cout << "\n** LOADING RESOURCES" << '\n';
|
||||
loadSounds();
|
||||
loadMusics();
|
||||
loadSurfaces();
|
||||
loadPalettes();
|
||||
loadTextFiles();
|
||||
loadAnimations();
|
||||
loadRooms();
|
||||
createText();
|
||||
std::cout << "\n** RESOURCES LOADED" << '\n';
|
||||
if (loading_mode_ == LoadingMode::EAGER) {
|
||||
calculateTotal();
|
||||
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
|
||||
std::cout << "\n** LOADING RESOURCES" << '\n';
|
||||
loadSounds();
|
||||
loadMusics();
|
||||
loadSurfaces();
|
||||
loadPalettes();
|
||||
loadTextFiles();
|
||||
loadAnimations();
|
||||
loadRooms();
|
||||
createText();
|
||||
std::cout << "\n** RESOURCES LOADED" << '\n';
|
||||
} else {
|
||||
std::cout << "\n** LAZY LOADING MODE **\n";
|
||||
initLazyStubs();
|
||||
createText(); // Carga fuentes bajo demanda a través de los getters lazy
|
||||
std::cout << "\n** RESOURCE STUBS READY" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
// Recarga todos los recursos
|
||||
@@ -80,10 +87,13 @@ namespace Resource {
|
||||
}
|
||||
|
||||
// Obtiene el sonido a partir de un nombre
|
||||
auto Cache::getSound(const std::string& name) -> JA_Sound_t* { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto Cache::getSound(const std::string& name) -> JA_Sound_t* {
|
||||
auto it = std::ranges::find_if(sounds_, [&name](const auto& s) -> bool { return s.name == name; });
|
||||
|
||||
if (it != sounds_.end()) {
|
||||
if (loading_mode_ == LoadingMode::LAZY && it->sound == nullptr) {
|
||||
loadSoundByName(name);
|
||||
}
|
||||
return it->sound;
|
||||
}
|
||||
|
||||
@@ -92,10 +102,13 @@ namespace Resource {
|
||||
}
|
||||
|
||||
// Obtiene la música a partir de un nombre
|
||||
auto Cache::getMusic(const std::string& name) -> JA_Music_t* { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto Cache::getMusic(const std::string& name) -> JA_Music_t* {
|
||||
auto it = std::ranges::find_if(musics_, [&name](const auto& m) -> bool { return m.name == name; });
|
||||
|
||||
if (it != musics_.end()) {
|
||||
if (loading_mode_ == LoadingMode::LAZY && it->music == nullptr) {
|
||||
loadMusicByName(name);
|
||||
}
|
||||
return it->music;
|
||||
}
|
||||
|
||||
@@ -104,10 +117,13 @@ namespace Resource {
|
||||
}
|
||||
|
||||
// Obtiene la surface a partir de un nombre
|
||||
auto Cache::getSurface(const std::string& name) -> std::shared_ptr<Surface> { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto Cache::getSurface(const std::string& name) -> std::shared_ptr<Surface> {
|
||||
auto it = std::ranges::find_if(surfaces_, [&name](const auto& t) -> bool { return t.name == name; });
|
||||
|
||||
if (it != surfaces_.end()) {
|
||||
if (loading_mode_ == LoadingMode::LAZY && it->surface == nullptr) {
|
||||
loadSurfaceByName(name);
|
||||
}
|
||||
return it->surface;
|
||||
}
|
||||
|
||||
@@ -116,10 +132,13 @@ namespace Resource {
|
||||
}
|
||||
|
||||
// Obtiene la paleta a partir de un nombre
|
||||
auto Cache::getPalette(const std::string& name) -> Palette { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto Cache::getPalette(const std::string& name) -> Palette {
|
||||
auto it = std::ranges::find_if(palettes_, [&name](const auto& t) -> bool { return t.name == name; });
|
||||
|
||||
if (it != palettes_.end()) {
|
||||
if (loading_mode_ == LoadingMode::LAZY && !it->loaded) {
|
||||
loadPaletteByName(name);
|
||||
}
|
||||
return it->palette;
|
||||
}
|
||||
|
||||
@@ -128,10 +147,13 @@ namespace Resource {
|
||||
}
|
||||
|
||||
// Obtiene el fichero de texto a partir de un nombre
|
||||
auto Cache::getTextFile(const std::string& name) -> std::shared_ptr<Text::File> { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto Cache::getTextFile(const std::string& name) -> std::shared_ptr<Text::File> {
|
||||
auto it = std::ranges::find_if(text_files_, [&name](const auto& t) -> bool { return t.name == name; });
|
||||
|
||||
if (it != text_files_.end()) {
|
||||
if (loading_mode_ == LoadingMode::LAZY && it->text_file == nullptr) {
|
||||
loadTextFileByName(name);
|
||||
}
|
||||
return it->text_file;
|
||||
}
|
||||
|
||||
@@ -152,10 +174,13 @@ namespace Resource {
|
||||
}
|
||||
|
||||
// Obtiene los datos de animación parseados a partir de un nombre
|
||||
auto Cache::getAnimationData(const std::string& name) -> const AnimationResource& { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto Cache::getAnimationData(const std::string& name) -> const AnimationResource& {
|
||||
auto it = std::ranges::find_if(animations_, [&name](const auto& a) -> bool { return a.name == name; });
|
||||
|
||||
if (it != animations_.end()) {
|
||||
if (loading_mode_ == LoadingMode::LAZY && it->yaml_data.empty()) {
|
||||
loadAnimationByName(name);
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
@@ -164,10 +189,13 @@ namespace Resource {
|
||||
}
|
||||
|
||||
// Obtiene la habitación a partir de un nombre
|
||||
auto Cache::getRoom(const std::string& name) -> std::shared_ptr<Room::Data> { // NOLINT(readability-convert-member-functions-to-static)
|
||||
auto Cache::getRoom(const std::string& name) -> std::shared_ptr<Room::Data> {
|
||||
auto it = std::ranges::find_if(rooms_, [&name](const auto& r) -> bool { return r.name == name; });
|
||||
|
||||
if (it != rooms_.end()) {
|
||||
if (loading_mode_ == LoadingMode::LAZY && it->room == nullptr) {
|
||||
loadRoomByName(name);
|
||||
}
|
||||
return it->room;
|
||||
}
|
||||
|
||||
@@ -205,6 +233,11 @@ namespace Resource {
|
||||
|
||||
// Obtiene todas las habitaciones
|
||||
auto Cache::getRooms() -> std::vector<RoomResource>& {
|
||||
if (loading_mode_ == LoadingMode::LAZY) {
|
||||
for (auto& r : rooms_) {
|
||||
if (r.room == nullptr) { loadRoomByName(r.name); }
|
||||
}
|
||||
}
|
||||
return rooms_;
|
||||
}
|
||||
|
||||
@@ -530,4 +563,144 @@ namespace Resource {
|
||||
checkEvents();
|
||||
}
|
||||
|
||||
// --- Modo lazy: stubs y cargadores bajo demanda ---------------------------
|
||||
|
||||
// Rellena los vectores de recursos con entradas name-only (sin contenido)
|
||||
void Cache::initLazyStubs() {
|
||||
sounds_.clear();
|
||||
musics_.clear();
|
||||
surfaces_.clear();
|
||||
palettes_.clear();
|
||||
text_files_.clear();
|
||||
animations_.clear();
|
||||
rooms_.clear();
|
||||
|
||||
for (const auto& l : List::get()->getListByType(List::Type::SOUND)) {
|
||||
sounds_.emplace_back(SoundResource{.name = getFileName(l), .sound = nullptr});
|
||||
}
|
||||
for (const auto& l : List::get()->getListByType(List::Type::MUSIC)) {
|
||||
musics_.emplace_back(MusicResource{.name = getFileName(l), .music = nullptr});
|
||||
}
|
||||
for (const auto& l : List::get()->getListByType(List::Type::BITMAP)) {
|
||||
surfaces_.emplace_back(SurfaceResource{.name = getFileName(l), .surface = nullptr});
|
||||
}
|
||||
for (const auto& l : List::get()->getListByType(List::Type::PALETTE)) {
|
||||
palettes_.emplace_back(ResourcePalette{.name = getFileName(l)});
|
||||
}
|
||||
for (const auto& l : List::get()->getListByType(List::Type::FONT)) {
|
||||
text_files_.emplace_back(TextFileResource{.name = getFileName(l), .text_file = nullptr});
|
||||
}
|
||||
for (const auto& l : List::get()->getListByType(List::Type::ANIMATION)) {
|
||||
animations_.emplace_back(AnimationResource{.name = getFileName(l), .yaml_data = {}});
|
||||
}
|
||||
for (const auto& l : List::get()->getListByType(List::Type::ROOM)) {
|
||||
rooms_.emplace_back(RoomResource{.name = getFileName(l), .room = nullptr});
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::loadSoundByName(const std::string& name) {
|
||||
auto it = std::ranges::find_if(sounds_, [&name](const auto& s) { return s.name == name; });
|
||||
if (it == sounds_.end()) { return; }
|
||||
auto path = List::get()->get(name);
|
||||
try {
|
||||
auto bytes = Helper::loadFile(path);
|
||||
JA_Sound_t* sound = nullptr;
|
||||
if (!bytes.empty()) { sound = JA_LoadSound(bytes.data(), static_cast<Uint32>(bytes.size())); }
|
||||
if (sound == nullptr) { sound = JA_LoadSound(path.c_str()); }
|
||||
if (sound == nullptr) { throw std::runtime_error("Failed to decode audio file"); }
|
||||
it->sound = sound;
|
||||
std::cout << "[lazy] Sound loaded: " << name << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
throwLoadError("SOUND", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::loadMusicByName(const std::string& name) {
|
||||
auto it = std::ranges::find_if(musics_, [&name](const auto& m) { return m.name == name; });
|
||||
if (it == musics_.end()) { return; }
|
||||
auto path = List::get()->get(name);
|
||||
try {
|
||||
auto bytes = Helper::loadFile(path);
|
||||
JA_Music_t* music = nullptr;
|
||||
if (!bytes.empty()) { music = JA_LoadMusic(bytes.data(), static_cast<Uint32>(bytes.size())); }
|
||||
if (music == nullptr) { music = JA_LoadMusic(path.c_str()); }
|
||||
if (music == nullptr) { throw std::runtime_error("Failed to decode music file"); }
|
||||
it->music = music;
|
||||
std::cout << "[lazy] Music loaded: " << name << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
throwLoadError("MUSIC", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::loadSurfaceByName(const std::string& name) {
|
||||
auto it = std::ranges::find_if(surfaces_, [&name](const auto& s) { return s.name == name; });
|
||||
if (it == surfaces_.end()) { return; }
|
||||
auto path = List::get()->get(name);
|
||||
try {
|
||||
it->surface = std::make_shared<Surface>(path);
|
||||
it->surface->setTransparentColor(0);
|
||||
// Superficies con color transparente específico (replica el ajuste de loadSurfaces)
|
||||
if (name == "loading_screen_color.gif" || name == "ending1.gif" || name == "ending2.gif" ||
|
||||
name == "ending3.gif" || name == "ending4.gif" || name == "ending5.gif") {
|
||||
it->surface->setTransparentColor();
|
||||
} else if (name == "standard.gif") {
|
||||
it->surface->setTransparentColor(16);
|
||||
}
|
||||
std::cout << "[lazy] Surface loaded: " << name << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
throwLoadError("BITMAP", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::loadPaletteByName(const std::string& name) {
|
||||
auto it = std::ranges::find_if(palettes_, [&name](const auto& p) { return p.name == name; });
|
||||
if (it == palettes_.end()) { return; }
|
||||
auto path = List::get()->get(name);
|
||||
try {
|
||||
it->palette = readPalFile(path);
|
||||
it->loaded = true;
|
||||
std::cout << "[lazy] Palette loaded: " << name << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
throwLoadError("PALETTE", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::loadTextFileByName(const std::string& name) {
|
||||
auto it = std::ranges::find_if(text_files_, [&name](const auto& t) { return t.name == name; });
|
||||
if (it == text_files_.end()) { return; }
|
||||
auto path = List::get()->get(name);
|
||||
try {
|
||||
it->text_file = Text::loadTextFile(path);
|
||||
std::cout << "[lazy] TextFile loaded: " << name << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
throwLoadError("FONT", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::loadAnimationByName(const std::string& name) {
|
||||
auto it = std::ranges::find_if(animations_, [&name](const auto& a) { return a.name == name; });
|
||||
if (it == animations_.end()) { return; }
|
||||
auto path = List::get()->get(name);
|
||||
try {
|
||||
auto bytes = Helper::loadFile(path);
|
||||
if (bytes.empty()) { throw std::runtime_error("File is empty or could not be loaded"); }
|
||||
it->yaml_data = bytes;
|
||||
std::cout << "[lazy] Animation loaded: " << name << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
throwLoadError("ANIMATION", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::loadRoomByName(const std::string& name) {
|
||||
auto it = std::ranges::find_if(rooms_, [&name](const auto& r) { return r.name == name; });
|
||||
if (it == rooms_.end()) { return; }
|
||||
auto path = List::get()->get(name);
|
||||
try {
|
||||
it->room = std::make_shared<Room::Data>(Room::loadYAML(path));
|
||||
std::cout << "[lazy] Room loaded: " << name << '\n';
|
||||
} catch (const std::exception& e) {
|
||||
throwLoadError("ROOM", path, e);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Resource
|
||||
|
||||
@@ -11,9 +11,14 @@ namespace Resource {
|
||||
|
||||
class Cache {
|
||||
public:
|
||||
static void init(); // Inicialización singleton
|
||||
static void destroy(); // Destrucción singleton
|
||||
static auto get() -> Cache*; // Acceso al singleton
|
||||
enum class LoadingMode {
|
||||
EAGER, // Carga todos los recursos en init() (comportamiento por defecto, producción)
|
||||
LAZY // Sólo registra nombres; carga cada recurso la primera vez que se pide (desarrollo)
|
||||
};
|
||||
|
||||
static void init(LoadingMode mode = LoadingMode::EAGER); // Inicialización singleton
|
||||
static void destroy(); // Destrucción singleton
|
||||
static auto get() -> Cache*; // Acceso al singleton
|
||||
|
||||
auto getSound(const std::string& name) -> JA_Sound_t*; // Getters de recursos
|
||||
auto getMusic(const std::string& name) -> JA_Music_t*;
|
||||
@@ -57,6 +62,18 @@ namespace Resource {
|
||||
void loadRooms();
|
||||
void createText();
|
||||
|
||||
// Registro de stubs (modo lazy): sólo nombres, sin contenido
|
||||
void initLazyStubs();
|
||||
|
||||
// Cargadores bajo demanda (modo lazy): cargan un recurso individual por nombre
|
||||
void loadSoundByName(const std::string& name);
|
||||
void loadMusicByName(const std::string& name);
|
||||
void loadSurfaceByName(const std::string& name);
|
||||
void loadPaletteByName(const std::string& name);
|
||||
void loadTextFileByName(const std::string& name);
|
||||
void loadAnimationByName(const std::string& name);
|
||||
void loadRoomByName(const std::string& name);
|
||||
|
||||
// Métodos de limpieza
|
||||
void clear();
|
||||
void clearSounds();
|
||||
@@ -73,9 +90,11 @@ namespace Resource {
|
||||
[[noreturn]] static void throwLoadError(const std::string& asset_type, const std::string& file_path, const std::exception& e);
|
||||
|
||||
// Constructor y destructor
|
||||
Cache();
|
||||
explicit Cache(LoadingMode mode);
|
||||
~Cache() = default;
|
||||
|
||||
LoadingMode loading_mode_ = LoadingMode::EAGER;
|
||||
|
||||
// Singleton instance
|
||||
static Cache* cache;
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@ struct SurfaceResource {
|
||||
|
||||
// Estructura para almacenar objetos Palette y su nombre
|
||||
struct ResourcePalette {
|
||||
std::string name; // Nombre de la surface
|
||||
Palette palette{}; // Paleta
|
||||
std::string name; // Nombre de la surface
|
||||
Palette palette{}; // Paleta
|
||||
bool loaded{false}; // Usado por el modo lazy para saber si ya se ha leído del disco
|
||||
};
|
||||
|
||||
// Estructura para almacenar ficheros TextFile y su nombre
|
||||
|
||||
@@ -134,6 +134,7 @@ void Debug::loadFromFile() {
|
||||
spawn_settings_.spawn_y = Defaults::Game::Player::SPAWN_Y;
|
||||
spawn_settings_.flip = Defaults::Game::Player::SPAWN_FLIP;
|
||||
initial_scene_ = SceneManager::Scene::GAME;
|
||||
lazy_loading_ = false;
|
||||
|
||||
std::ifstream file(debug_file_path_);
|
||||
if (!file.good()) {
|
||||
@@ -162,6 +163,9 @@ void Debug::loadFromFile() {
|
||||
if (yaml.contains("initial_scene")) {
|
||||
initial_scene_ = sceneFromString(yaml["initial_scene"].get_value<std::string>());
|
||||
}
|
||||
if (yaml.contains("lazy_loading")) {
|
||||
lazy_loading_ = yaml["lazy_loading"].get_value<bool>();
|
||||
}
|
||||
} catch (...) {
|
||||
// YAML inválido: resetear a defaults y sobreescribir
|
||||
spawn_settings_.room = Defaults::Game::Room::INITIAL;
|
||||
@@ -169,6 +173,7 @@ void Debug::loadFromFile() {
|
||||
spawn_settings_.spawn_y = Defaults::Game::Player::SPAWN_Y;
|
||||
spawn_settings_.flip = Defaults::Game::Player::SPAWN_FLIP;
|
||||
initial_scene_ = SceneManager::Scene::GAME;
|
||||
lazy_loading_ = false;
|
||||
saveToFile();
|
||||
}
|
||||
}
|
||||
@@ -184,6 +189,7 @@ void Debug::saveToFile() const {
|
||||
file << "spawn_y: " << (spawn_settings_.spawn_y / Tile::SIZE) << " # en tiles\n";
|
||||
file << "spawn_flip: " << ((spawn_settings_.flip == Flip::RIGHT) ? "right" : "left") << "\n";
|
||||
file << "initial_scene: " << sceneToString(initial_scene_) << "\n";
|
||||
file << "lazy_loading: " << (lazy_loading_ ? "true" : "false") << " # carga perezosa de recursos (dev)\n";
|
||||
}
|
||||
|
||||
#endif // _DEBUG
|
||||
@@ -47,6 +47,7 @@ class Debug {
|
||||
void setSpawnSettings(const SpawnSettings& s) { spawn_settings_ = s; } // Establece los valores de spawn
|
||||
[[nodiscard]] auto getInitialScene() const -> SceneManager::Scene { return initial_scene_; } // Obtiene la escena inicial de debug
|
||||
void setInitialScene(SceneManager::Scene s) { initial_scene_ = s; } // Establece la escena inicial de debug
|
||||
[[nodiscard]] auto getLazyLoading() const -> bool { return lazy_loading_; } // Indica si el modo lazy de recursos está activo
|
||||
|
||||
private:
|
||||
static Debug* debug; // [SINGLETON] Objeto privado
|
||||
@@ -64,6 +65,7 @@ class Debug {
|
||||
std::string debug_file_path_; // Ruta del archivo debug.yaml
|
||||
SpawnSettings spawn_settings_; // Configuración de spawn para debug
|
||||
SceneManager::Scene initial_scene_ = SceneManager::Scene::GAME; // Escena inicial en debug
|
||||
bool lazy_loading_ = false; // Carga lazy de recursos (dev)
|
||||
};
|
||||
|
||||
#endif // _DEBUG
|
||||
@@ -160,8 +160,21 @@ Director::Director() {
|
||||
// Crea los objetos
|
||||
Screen::init();
|
||||
|
||||
#ifdef _DEBUG
|
||||
// En debug inicializamos Debug antes de Cache para leer el flag lazy_loading
|
||||
Debug::init();
|
||||
Debug::get()->setDebugFile(Resource::List::get()->get("debug.yaml"));
|
||||
Debug::get()->loadFromFile();
|
||||
#endif
|
||||
|
||||
// Initialize resources (works for both release and development)
|
||||
#ifdef _DEBUG
|
||||
Resource::Cache::init(Debug::get()->getLazyLoading()
|
||||
? Resource::Cache::LoadingMode::LAZY
|
||||
: Resource::Cache::LoadingMode::EAGER);
|
||||
#else
|
||||
Resource::Cache::init();
|
||||
#endif
|
||||
Notifier::init("", "8bithud");
|
||||
RenderInfo::init();
|
||||
Console::init("8bithud");
|
||||
@@ -182,9 +195,6 @@ Director::Director() {
|
||||
Input::get()->applyGamepadBindingsFromOptions();
|
||||
|
||||
#ifdef _DEBUG
|
||||
Debug::init();
|
||||
Debug::get()->setDebugFile(Resource::List::get()->get("debug.yaml"));
|
||||
Debug::get()->loadFromFile();
|
||||
SceneManager::current = Debug::get()->getInitialScene();
|
||||
MapEditor::init();
|
||||
#endif
|
||||
|
||||
@@ -1131,6 +1131,7 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
std::string val = toLower(value);
|
||||
|
||||
if (property == "BGCOLOR") {
|
||||
val = colorToString(stringToColor(val)); // Normaliza a nombre canónico (acepta nombres e índices)
|
||||
room_data_.bg_color = val;
|
||||
room_->setBgColor(val);
|
||||
autosave();
|
||||
@@ -1138,6 +1139,7 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
}
|
||||
|
||||
if (property == "BORDER") {
|
||||
val = colorToString(stringToColor(val));
|
||||
room_data_.border_color = val;
|
||||
Screen::get()->setBorderColor(stringToColor(val));
|
||||
autosave();
|
||||
@@ -1145,6 +1147,7 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
}
|
||||
|
||||
if (property == "ITEMCOLOR1") {
|
||||
val = colorToString(stringToColor(val));
|
||||
room_data_.item_color1 = val;
|
||||
room_->setItemColors(room_data_.item_color1, room_data_.item_color2);
|
||||
autosave();
|
||||
@@ -1152,6 +1155,7 @@ auto MapEditor::setRoomProperty(const std::string& property, const std::string&
|
||||
}
|
||||
|
||||
if (property == "ITEMCOLOR2") {
|
||||
val = colorToString(stringToColor(val));
|
||||
room_data_.item_color2 = val;
|
||||
room_->setItemColors(room_data_.item_color1, room_data_.item_color2);
|
||||
autosave();
|
||||
|
||||
@@ -76,11 +76,8 @@ void Player::move(float delta_time) {
|
||||
case State::ON_SLOPE:
|
||||
moveOnSlope(delta_time);
|
||||
break;
|
||||
case State::JUMPING:
|
||||
moveJumping(delta_time);
|
||||
break;
|
||||
case State::FALLING:
|
||||
moveFalling(delta_time);
|
||||
case State::ON_AIR:
|
||||
moveOnAir(delta_time);
|
||||
break;
|
||||
}
|
||||
syncSpriteAndCollider(); // Actualiza la posición del sprite y las colisiones
|
||||
@@ -95,11 +92,8 @@ void Player::move(float delta_time) {
|
||||
case State::ON_SLOPE:
|
||||
Debug::get()->set("P.STATE", "ON_SLOPE");
|
||||
break;
|
||||
case State::JUMPING:
|
||||
Debug::get()->set("P.STATE", "JUMPING");
|
||||
break;
|
||||
case State::FALLING:
|
||||
Debug::get()->set("P.STATE", "FALLING");
|
||||
case State::ON_AIR:
|
||||
Debug::get()->set("P.STATE", "ON_AIR");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -117,7 +111,8 @@ void Player::handleConveyorBelts() {
|
||||
|
||||
void Player::handleShouldFall() {
|
||||
if (!isOnFloor() and (state_ == State::ON_GROUND || state_ == State::ON_SLOPE)) {
|
||||
transitionToState(State::FALLING);
|
||||
vy_ = 0.0F;
|
||||
transitionToState(State::ON_AIR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,37 +123,24 @@ void Player::transitionToState(State state) {
|
||||
switch (state) {
|
||||
case State::ON_GROUND:
|
||||
vy_ = 0;
|
||||
handleDeathByFalling();
|
||||
resetSoundControllersOnLanding();
|
||||
if (previous_state_ == State::ON_AIR) {
|
||||
Audio::get()->playSound(land_sound_, Audio::Group::GAME);
|
||||
}
|
||||
current_slope_ = nullptr;
|
||||
break;
|
||||
case State::ON_SLOPE:
|
||||
vy_ = 0;
|
||||
handleDeathByFalling();
|
||||
resetSoundControllersOnLanding();
|
||||
if (previous_state_ == State::ON_AIR) {
|
||||
Audio::get()->playSound(land_sound_, Audio::Group::GAME);
|
||||
}
|
||||
updateCurrentSlope();
|
||||
if (current_slope_ == nullptr) {
|
||||
// Los pies no coinciden con ninguna rampa: tratar como suelo plano
|
||||
state_ = State::ON_GROUND;
|
||||
}
|
||||
break;
|
||||
case State::JUMPING:
|
||||
// Puede saltar desde ON_GROUND o ON_SLOPE
|
||||
if (previous_state_ == State::ON_GROUND || previous_state_ == State::ON_SLOPE) {
|
||||
vy_ = -MAX_VY;
|
||||
last_grounded_position_ = y_;
|
||||
updateVelocity();
|
||||
jump_sound_ctrl_.start();
|
||||
current_slope_ = nullptr;
|
||||
}
|
||||
break;
|
||||
case State::FALLING:
|
||||
fall_start_position_ = static_cast<int>(y_);
|
||||
case State::ON_AIR:
|
||||
last_grounded_position_ = static_cast<int>(y_);
|
||||
vy_ = MAX_VY;
|
||||
vx_ = 0.0F;
|
||||
jump_sound_ctrl_.reset();
|
||||
fall_sound_ctrl_.start(y_);
|
||||
current_slope_ = nullptr;
|
||||
break;
|
||||
}
|
||||
@@ -172,48 +154,48 @@ void Player::updateState(float delta_time) {
|
||||
case State::ON_SLOPE:
|
||||
updateOnSlope(delta_time);
|
||||
break;
|
||||
case State::JUMPING:
|
||||
updateJumping(delta_time);
|
||||
break;
|
||||
case State::FALLING:
|
||||
updateFalling(delta_time);
|
||||
case State::ON_AIR:
|
||||
updateOnAir(delta_time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Inicia un salto desde ON_GROUND/ON_SLOPE: velocidad inicial hacia arriba + sonido + transición
|
||||
void Player::startJump() {
|
||||
vy_ = JUMP_VELOCITY;
|
||||
last_grounded_position_ = y_;
|
||||
Audio::get()->playSound(jump_sound_, Audio::Group::GAME);
|
||||
transitionToState(State::ON_AIR);
|
||||
}
|
||||
|
||||
// Actualización lógica del estado ON_GROUND
|
||||
void Player::updateOnGround(float delta_time) {
|
||||
(void)delta_time; // No usado en este método, pero se mantiene por consistencia
|
||||
handleConveyorBelts(); // Gestiona las cintas transportadoras
|
||||
handleShouldFall(); // Verifica si debe caer (no tiene suelo)
|
||||
|
||||
// Verifica si el jugador quiere saltar
|
||||
if (wanna_jump_) { transitionToState(State::JUMPING); }
|
||||
// El salto tiene prioridad sobre la caída por falta de suelo
|
||||
if (wanna_jump_) {
|
||||
startJump();
|
||||
return;
|
||||
}
|
||||
handleShouldFall(); // Verifica si debe caer (no tiene suelo)
|
||||
}
|
||||
|
||||
// Actualización lógica del estado ON_SLOPE
|
||||
void Player::updateOnSlope(float delta_time) {
|
||||
(void)delta_time; // No usado en este método, pero se mantiene por consistencia
|
||||
|
||||
if (wanna_jump_) {
|
||||
startJump();
|
||||
return;
|
||||
}
|
||||
handleShouldFall();
|
||||
// NOTA: No llamamos handleShouldFall() aquí porque moveOnSlope() ya maneja
|
||||
// todas las condiciones de salida de la rampa (out of bounds, transición a superficie plana)
|
||||
|
||||
// Verifica si el jugador quiere saltar
|
||||
if (wanna_jump_) { transitionToState(State::JUMPING); }
|
||||
}
|
||||
|
||||
// Actualización lógica del estado JUMPING
|
||||
void Player::updateJumping(float delta_time) {
|
||||
auto_movement_ = false; // Desactiva el movimiento automático durante el salto
|
||||
playJumpSound(delta_time); // Reproduce los sonidos de salto
|
||||
handleJumpEnd(); // Verifica si el salto ha terminado (alcanzó la altura inicial)
|
||||
}
|
||||
|
||||
// Actualización lógica del estado FALLING
|
||||
void Player::updateFalling(float delta_time) {
|
||||
auto_movement_ = false; // Desactiva el movimiento automático durante la caída
|
||||
playFallSound(delta_time); // Reproduce los sonidos de caída
|
||||
// Actualización lógica del estado ON_AIR
|
||||
void Player::updateOnAir(float delta_time) {
|
||||
(void)delta_time;
|
||||
auto_movement_ = false; // Desactiva el movimiento automático en el aire
|
||||
}
|
||||
|
||||
// Movimiento físico del estado ON_GROUND
|
||||
@@ -256,7 +238,8 @@ void Player::moveOnSlope(float delta_time) {
|
||||
// Verificar rampa válida antes de comprobar velocidad: si no hay rampa siempre caer,
|
||||
// independientemente de si hay o no input (evita bloqueo con vx_=0 y slope null)
|
||||
if (current_slope_ == nullptr) {
|
||||
transitionToState(State::FALLING);
|
||||
vy_ = 0.0F;
|
||||
transitionToState(State::ON_AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -320,7 +303,8 @@ void Player::moveOnSlope(float delta_time) {
|
||||
transitionToState(State::ON_GROUND);
|
||||
} else {
|
||||
// Sin soporte: empezar a caer
|
||||
transitionToState(State::FALLING);
|
||||
vy_ = 0.0F;
|
||||
transitionToState(State::ON_AIR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -332,69 +316,39 @@ void Player::moveOnSlope(float delta_time) {
|
||||
}*/
|
||||
}
|
||||
|
||||
// Movimiento físico del estado JUMPING
|
||||
void Player::moveJumping(float delta_time) {
|
||||
// Movimiento horizontal
|
||||
// Movimiento físico del estado ON_AIR
|
||||
// El jugador puede moverse horizontalmente en el aire y la gravedad siempre actúa.
|
||||
void Player::moveOnAir(float delta_time) {
|
||||
// Movimiento horizontal libre según wanna_go_ (permite girar en el aire)
|
||||
updateVelocity();
|
||||
applyHorizontalMovement(delta_time);
|
||||
|
||||
// Movimiento vertical
|
||||
// Gravedad
|
||||
applyGravity(delta_time);
|
||||
|
||||
const float DISPLACEMENT_Y = vy_ * delta_time;
|
||||
// Movimiento vertical hacia arriba
|
||||
|
||||
// Subiendo: comprobar techo
|
||||
if (vy_ < 0.0F) {
|
||||
const SDL_FRect PROJECTION = getProjection(Direction::UP, DISPLACEMENT_Y);
|
||||
|
||||
// Comprueba la colisión
|
||||
const int POS = room_->checkBottomSurfaces(PROJECTION);
|
||||
|
||||
// Calcula la nueva posición
|
||||
if (POS == Collision::NONE) {
|
||||
// Si no hay colisión
|
||||
y_ += DISPLACEMENT_Y;
|
||||
} else {
|
||||
// Si hay colisión lo mueve hasta donde no colisiona -> FALLING
|
||||
// Choque con techo: se pega por debajo y empieza a caer
|
||||
y_ = POS + 1;
|
||||
transitionToState(State::FALLING);
|
||||
vy_ = 0.0F;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Movimiento vertical hacia abajo
|
||||
else if (vy_ > 0.0F) {
|
||||
// Crea el rectangulo de proyección en el eje Y para ver si colisiona
|
||||
|
||||
// Bajando: comprobar aterrizaje en superficies y rampas
|
||||
if (vy_ > 0.0F) {
|
||||
const SDL_FRect PROJECTION = getProjection(Direction::DOWN, DISPLACEMENT_Y);
|
||||
|
||||
// JUMPING colisiona con rampas solo si vx_ == 0
|
||||
if (vx_ == 0.0F) {
|
||||
handleLandingFromAir(DISPLACEMENT_Y, PROJECTION);
|
||||
} else {
|
||||
// Comprueba la colisión con las superficies y las cintas transportadoras (sin rampas)
|
||||
// Extendemos 1px hacia arriba para detectar suelos traversados ligeramente al
|
||||
// entrar horizontalmente (consecuencia del margen h=HEIGHT-1 en la proyección horizontal)
|
||||
const SDL_FRect ADJ = {.x = PROJECTION.x, .y = PROJECTION.y - 1.0F, .w = PROJECTION.w, .h = PROJECTION.h + 1.0F};
|
||||
const float POS = std::max(room_->checkTopSurfaces(ADJ), room_->checkAutoSurfaces(ADJ));
|
||||
if (POS != Collision::NONE) {
|
||||
// Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre la superficie
|
||||
y_ = POS - HEIGHT;
|
||||
transitionToState(State::ON_GROUND);
|
||||
} else {
|
||||
// Esta saltando con movimiento horizontal y no hay colisión con los muros
|
||||
// Calcula la nueva posición (atraviesa rampas)
|
||||
y_ += DISPLACEMENT_Y;
|
||||
}
|
||||
}
|
||||
handleLandingFromAir(DISPLACEMENT_Y, PROJECTION);
|
||||
}
|
||||
}
|
||||
|
||||
// Movimiento físico del estado FALLING
|
||||
void Player::moveFalling(float delta_time) {
|
||||
// Crea el rectangulo de proyección en el eje Y para ver si colisiona
|
||||
const float DISPLACEMENT = vy_ * delta_time;
|
||||
const SDL_FRect PROJECTION = getProjection(Direction::DOWN, DISPLACEMENT);
|
||||
|
||||
// Comprueba aterrizaje en superficies y rampas
|
||||
handleLandingFromAir(DISPLACEMENT, PROJECTION);
|
||||
}
|
||||
|
||||
// Comprueba si está situado en alguno de los cuatro bordes de la habitación
|
||||
auto Player::handleBorders() -> Room::Border {
|
||||
if (x_ < PlayArea::LEFT) {
|
||||
@@ -410,10 +364,8 @@ auto Player::handleBorders() -> Room::Border {
|
||||
}
|
||||
|
||||
if (y_ + HEIGHT > PlayArea::BOTTOM) {
|
||||
// Si llega en estado terminal, muere y no cruza
|
||||
const bool SHOULD_DIE = static_cast<int>(y_) - last_grounded_position_ > MAX_FALLING_HEIGHT;
|
||||
if (SHOULD_DIE) { markAsDead(); }
|
||||
return is_alive_ ? Room::Border::BOTTOM : Room::Border::NONE;
|
||||
// Restricción de muerte por altura de caída desactivada
|
||||
return Room::Border::BOTTOM;
|
||||
}
|
||||
|
||||
return Room::Border::NONE;
|
||||
@@ -454,9 +406,8 @@ void Player::switchBorders() {
|
||||
|
||||
// Aplica gravedad al jugador
|
||||
void Player::applyGravity(float delta_time) {
|
||||
// La gravedad solo se aplica cuando el jugador esta saltando
|
||||
// Nunca mientras cae o esta de pie
|
||||
if (state_ == State::JUMPING) {
|
||||
// La gravedad solo se aplica cuando el jugador esta en el aire
|
||||
if (state_ == State::ON_AIR) {
|
||||
vy_ += GRAVITY_FORCE * delta_time;
|
||||
vy_ = std::min(vy_, MAX_VY);
|
||||
}
|
||||
@@ -464,37 +415,13 @@ void Player::applyGravity(float delta_time) {
|
||||
|
||||
// Establece la animación del jugador
|
||||
void Player::animate(float delta_time) { // NOLINT(readability-make-member-function-const)
|
||||
if (vx_ != 0) {
|
||||
if (state_ == State::ON_AIR) {
|
||||
sprite_->setCurrentAnimation("jump");
|
||||
} else if (vx_ != 0) {
|
||||
sprite_->setCurrentAnimation("default");
|
||||
sprite_->update(delta_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba si ha finalizado el salto al alcanzar la altura de inicio
|
||||
void Player::handleJumpEnd() {
|
||||
// Si el jugador vuelve EXACTAMENTE a la altura inicial, debe CONTINUAR en JUMPING
|
||||
// Solo cuando la SUPERA (desciende más allá) cambia a FALLING
|
||||
if (state_ == State::JUMPING && vy_ > 0.0F && static_cast<int>(y_) > last_grounded_position_) {
|
||||
transitionToState(State::FALLING);
|
||||
}
|
||||
}
|
||||
|
||||
// Calcula y reproduce el sonido de salto basado en tiempo transcurrido
|
||||
void Player::playJumpSound(float delta_time) { // NOLINT(readability-convert-member-functions-to-static)
|
||||
size_t sound_index;
|
||||
if (jump_sound_ctrl_.shouldPlay(delta_time, sound_index)) {
|
||||
if (sound_index < jumping_sound_.size()) {
|
||||
Audio::get()->playSound(jumping_sound_[sound_index], Audio::Group::GAME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calcula y reproduce el sonido de caída basado en distancia vertical recorrida
|
||||
void Player::playFallSound(float delta_time) { // NOLINT(readability-convert-member-functions-to-static)
|
||||
size_t sound_index;
|
||||
if (fall_sound_ctrl_.shouldPlay(delta_time, y_, sound_index)) {
|
||||
if (sound_index < falling_sound_.size()) {
|
||||
Audio::get()->playSound(falling_sound_[sound_index], Audio::Group::GAME);
|
||||
}
|
||||
} else {
|
||||
sprite_->setCurrentAnimation("stand");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -665,98 +592,10 @@ void Player::updateFeet() {
|
||||
.y = y_ + HEIGHT};
|
||||
}
|
||||
|
||||
// Inicializa los sonidos de salto y caida
|
||||
// Inicializa los sonidos de salto y aterrizaje
|
||||
void Player::initSounds() { // NOLINT(readability-convert-member-functions-to-static)
|
||||
for (int i = 0; i < 24; ++i) {
|
||||
std::string sound_file = "jump" + std::to_string(i + 1) + ".wav";
|
||||
jumping_sound_[i] = Resource::Cache::get()->getSound(sound_file);
|
||||
|
||||
if (i >= 10) { // i+1 >= 11
|
||||
falling_sound_[i - 10] = Resource::Cache::get()->getSound(sound_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementación de JumpSoundController::start
|
||||
void Player::JumpSoundController::start() {
|
||||
current_index = 0;
|
||||
elapsed_time = 0.0F;
|
||||
active = true;
|
||||
}
|
||||
|
||||
// Implementación de JumpSoundController::reset
|
||||
void Player::JumpSoundController::reset() {
|
||||
active = false;
|
||||
current_index = 0;
|
||||
elapsed_time = 0.0F;
|
||||
}
|
||||
|
||||
// Implementación de JumpSoundController::shouldPlay
|
||||
auto Player::JumpSoundController::shouldPlay(float delta_time, size_t& out_index) -> bool {
|
||||
if (!active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Acumula el tiempo transcurrido durante el salto
|
||||
elapsed_time += delta_time;
|
||||
|
||||
// Calcula qué sonido debería estar sonando según el tiempo
|
||||
size_t target_index = FIRST_SOUND + static_cast<size_t>((elapsed_time / SECONDS_PER_SOUND));
|
||||
target_index = std::min(target_index, LAST_SOUND);
|
||||
|
||||
// Reproduce si hemos avanzado a un nuevo sonido
|
||||
if (target_index > current_index) {
|
||||
current_index = target_index;
|
||||
out_index = current_index;
|
||||
return true; // NOLINT(readability-simplify-boolean-expr)
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implementación de FallSoundController::start
|
||||
void Player::FallSoundController::start(float start_y) {
|
||||
current_index = 0;
|
||||
distance_traveled = 0.0F;
|
||||
last_y = start_y;
|
||||
active = true;
|
||||
}
|
||||
|
||||
// Implementación de FallSoundController::reset
|
||||
void Player::FallSoundController::reset() {
|
||||
active = false;
|
||||
current_index = 0;
|
||||
distance_traveled = 0.0F;
|
||||
}
|
||||
|
||||
// Implementación de FallSoundController::shouldPlay
|
||||
auto Player::FallSoundController::shouldPlay(float delta_time, float current_y, size_t& out_index) -> bool {
|
||||
(void)delta_time; // No usado actualmente, pero recibido por consistencia
|
||||
|
||||
if (!active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Acumula la distancia recorrida (solo hacia abajo)
|
||||
if (current_y > last_y) {
|
||||
distance_traveled += (current_y - last_y);
|
||||
}
|
||||
last_y = current_y;
|
||||
|
||||
// Calcula qué sonido debería estar sonando según el intervalo
|
||||
size_t target_index = FIRST_SOUND + static_cast<size_t>((distance_traveled / PIXELS_PER_SOUND));
|
||||
|
||||
// El sonido a reproducir se limita a LAST_SOUND (13), pero el índice interno sigue creciendo
|
||||
size_t sound_to_play = std::min(target_index, LAST_SOUND);
|
||||
|
||||
// Reproduce si hemos avanzado a un nuevo índice (permite repetición de sonido 13)
|
||||
if (target_index > current_index) {
|
||||
current_index = target_index; // Guardamos el índice real (puede ser > LAST_SOUND)
|
||||
out_index = sound_to_play; // Pero reproducimos LAST_SOUND cuando corresponde
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
jump_sound_ = Resource::Cache::get()->getSound("jump.wav");
|
||||
land_sound_ = Resource::Cache::get()->getSound("land.wav");
|
||||
}
|
||||
|
||||
// Aplica los valores de spawn al jugador
|
||||
@@ -810,14 +649,6 @@ void Player::placeSprite() {
|
||||
sprite_->setPos(x_, y_);
|
||||
}
|
||||
|
||||
// Gestiona la muerta al ccaer desde muy alto
|
||||
void Player::handleDeathByFalling() {
|
||||
const int FALL_DISTANCE = static_cast<int>(y_) - last_grounded_position_;
|
||||
if (previous_state_ == State::FALLING && FALL_DISTANCE > MAX_FALLING_HEIGHT) {
|
||||
markAsDead(); // Muere si cae más de 32 píxeles
|
||||
}
|
||||
}
|
||||
|
||||
// Calcula la velocidad en x
|
||||
void Player::updateVelocity() {
|
||||
if (auto_movement_) {
|
||||
@@ -900,12 +731,6 @@ auto Player::handleLandingFromAir(float displacement, const SDL_FRect& projectio
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resetea los controladores de sonido al aterrizar
|
||||
void Player::resetSoundControllersOnLanding() {
|
||||
jump_sound_ctrl_.reset();
|
||||
fall_sound_ctrl_.reset();
|
||||
}
|
||||
|
||||
// Devuelve el rectangulo de proyeccion
|
||||
auto Player::getProjection(Direction direction, float displacement) -> SDL_FRect { // NOLINT(readability-convert-member-functions-to-static)
|
||||
switch (direction) {
|
||||
|
||||
@@ -21,8 +21,7 @@ class Player {
|
||||
enum class State {
|
||||
ON_GROUND, // En suelo plano o conveyor belt
|
||||
ON_SLOPE, // En rampa/pendiente
|
||||
JUMPING,
|
||||
FALLING,
|
||||
ON_AIR, // En el aire (saltando, cayendo o caminando al vacío)
|
||||
};
|
||||
|
||||
enum class Direction {
|
||||
@@ -34,10 +33,10 @@ class Player {
|
||||
};
|
||||
|
||||
// --- Constantes de física (públicas para permitir cálculos en structs) ---
|
||||
static constexpr float HORIZONTAL_VELOCITY = 40.0F; // Velocidad horizontal en pixels/segundo (0.6 * 66.67fps)
|
||||
static constexpr float MAX_VY = 80.0F; // Velocidad vertical máxima en pixels/segundo (1.2 * 66.67fps)
|
||||
static constexpr float JUMP_VELOCITY = -80.0F; // Velocidad inicial del salto en pixels/segundo
|
||||
static constexpr float GRAVITY_FORCE = 155.6F; // Fuerza de gravedad en pixels/segundo² (0.035 * 66.67²)
|
||||
static constexpr float HORIZONTAL_VELOCITY = 60.0F; // Velocidad horizontal en pixels/segundo
|
||||
static constexpr float MAX_VY = 160.0F; // Velocidad vertical máxima en pixels/segundo
|
||||
static constexpr float JUMP_VELOCITY = -140.0F; // Velocidad inicial del salto en pixels/segundo
|
||||
static constexpr float GRAVITY_FORCE = 360.0F; // Fuerza de gravedad en pixels/segundo²
|
||||
|
||||
struct SpawnData {
|
||||
float x = 0;
|
||||
@@ -55,37 +54,6 @@ class Player {
|
||||
std::shared_ptr<Room> room = nullptr;
|
||||
};
|
||||
|
||||
struct JumpSoundController {
|
||||
// Duración del salto calculada automáticamente con física: t = 2 * v0 / g
|
||||
static constexpr float JUMP_DURATION = (2.0F * MAX_VY) / GRAVITY_FORCE;
|
||||
static constexpr size_t FIRST_SOUND = 1; // Primer sonido a reproducir (índice 1)
|
||||
static constexpr size_t LAST_SOUND = 17; // Último sonido a reproducir (índice 17)
|
||||
static constexpr float SECONDS_PER_SOUND = JUMP_DURATION / (LAST_SOUND - FIRST_SOUND + 1);
|
||||
|
||||
size_t current_index = 0; // Índice del sonido actual
|
||||
float elapsed_time = 0.0F; // Tiempo transcurrido durante el salto
|
||||
bool active = false; // Indica si el controlador está activo
|
||||
|
||||
void start(); // Inicia el controlador
|
||||
void reset(); // Resetea el controlador
|
||||
auto shouldPlay(float delta_time, size_t& out_index) -> bool; // Comprueba si debe reproducir un sonido
|
||||
};
|
||||
|
||||
struct FallSoundController {
|
||||
static constexpr float PIXELS_PER_SOUND = 5.0F; // Intervalo de píxeles por sonido (configurable)
|
||||
static constexpr size_t FIRST_SOUND = 1; // Primer sonido a reproducir (índice 1)
|
||||
static constexpr size_t LAST_SOUND = 13; // Último sonido a reproducir (índice 13)
|
||||
|
||||
size_t current_index = 0; // Índice del sonido actual
|
||||
float distance_traveled = 0.0F; // Distancia acumulada durante la caída
|
||||
float last_y = 0.0F; // Última posición Y registrada
|
||||
bool active = false; // Indica si el controlador está activo
|
||||
|
||||
void start(float start_y); // Inicia el controlador
|
||||
void reset(); // Resetea el controlador
|
||||
auto shouldPlay(float delta_time, float current_y, size_t& out_index) -> bool; // Comprueba si debe reproducir un sonido
|
||||
};
|
||||
|
||||
// --- Constructor y Destructor ---
|
||||
explicit Player(const Data& player);
|
||||
~Player() = default;
|
||||
@@ -155,12 +123,9 @@ class Player {
|
||||
int last_grounded_position_ = 0; // Ultima posición en Y en la que se estaba en contacto con el suelo (hace doble función: tracking de caída + altura inicial del salto)
|
||||
|
||||
// --- Variables de renderizado y sonido ---
|
||||
Uint8 color_ = 0; // Color del jugador
|
||||
std::array<JA_Sound_t*, 24> jumping_sound_{}; // Array con todos los sonidos del salto
|
||||
std::array<JA_Sound_t*, 14> falling_sound_{}; // Array con todos los sonidos de la caída
|
||||
JumpSoundController jump_sound_ctrl_; // Controlador de sonidos de salto
|
||||
FallSoundController fall_sound_ctrl_; // Controlador de sonidos de caída
|
||||
int fall_start_position_ = 0; // Posición Y al iniciar la caída
|
||||
Uint8 color_ = 0; // Color del jugador
|
||||
JA_Sound_t* jump_sound_ = nullptr; // Sonido al iniciar el salto
|
||||
JA_Sound_t* land_sound_ = nullptr; // Sonido al aterrizar en el suelo
|
||||
|
||||
void handleConveyorBelts();
|
||||
void handleShouldFall();
|
||||
@@ -169,14 +134,12 @@ class Player {
|
||||
// --- Métodos de actualización por estado ---
|
||||
void updateOnGround(float delta_time); // Actualización lógica estado ON_GROUND
|
||||
void updateOnSlope(float delta_time); // Actualización lógica estado ON_SLOPE
|
||||
void updateJumping(float delta_time); // Actualización lógica estado JUMPING
|
||||
void updateFalling(float delta_time); // Actualización lógica estado FALLING
|
||||
void updateOnAir(float delta_time); // Actualización lógica estado ON_AIR
|
||||
|
||||
// --- Métodos de movimiento por estado ---
|
||||
void moveOnGround(float delta_time); // Movimiento físico estado ON_GROUND
|
||||
void moveOnSlope(float delta_time); // Movimiento físico estado ON_SLOPE
|
||||
void moveJumping(float delta_time); // Movimiento físico estado JUMPING
|
||||
void moveFalling(float delta_time); // Movimiento físico estado FALLING
|
||||
void moveOnAir(float delta_time); // Movimiento físico estado ON_AIR
|
||||
|
||||
// --- Funciones de inicialización ---
|
||||
void initSprite(const std::string& animations_path); // Inicializa el sprite del jugador
|
||||
@@ -188,6 +151,7 @@ class Player {
|
||||
|
||||
// --- Funciones de gestión de estado ---
|
||||
void transitionToState(State state); // Cambia el estado del jugador
|
||||
void startJump(); // Inicia el salto: velocidad inicial + sonido + transición a ON_AIR
|
||||
|
||||
// --- Funciones de física ---
|
||||
void applyGravity(float delta_time); // Aplica gravedad al jugador
|
||||
@@ -197,7 +161,6 @@ class Player {
|
||||
auto getProjection(Direction direction, float displacement) -> SDL_FRect; // Devuelve el rectangulo de proyeccion
|
||||
void applyHorizontalMovement(float delta_time); // Aplica movimiento horizontal con colisión de muros
|
||||
auto handleLandingFromAir(float displacement, const SDL_FRect& projection) -> bool; // Detecta aterrizaje en superficies y rampas
|
||||
void resetSoundControllersOnLanding(); // Resetea los controladores de sonido al aterrizar
|
||||
|
||||
// --- Funciones de detección de superficies ---
|
||||
auto isOnFloor() -> bool; // Comprueba si el jugador tiene suelo debajo de los pies
|
||||
@@ -216,11 +179,7 @@ class Player {
|
||||
// --- Funciones de finalización ---
|
||||
void animate(float delta_time); // Establece la animación del jugador
|
||||
auto handleBorders() -> Room::Border; // Comprueba si se halla en alguno de los cuatro bordes
|
||||
void handleJumpEnd(); // Comprueba si ha finalizado el salto al alcanzar la altura de inicio
|
||||
auto handleKillingTiles() -> bool; // Comprueba que el jugador no toque ningun tile de los que matan
|
||||
void playJumpSound(float delta_time); // Calcula y reproduce el sonido de salto
|
||||
void playFallSound(float delta_time); // Calcula y reproduce el sonido de caer
|
||||
void handleDeathByFalling(); // Gestiona la muerte al caer desde muy alto
|
||||
void updateVelocity(); // Calcula la velocidad en x
|
||||
void markAsDead(); // Marca al jugador como muerto
|
||||
};
|
||||
@@ -21,6 +21,13 @@ auto RoomLoader::convertRoomConnection(const std::string& value) -> std::string
|
||||
return value + ".yaml";
|
||||
}
|
||||
|
||||
// Lee un nodo de color tolerando tanto string ("red", "bright_blue") como entero (índice de paleta)
|
||||
static auto readColorNode(const fkyaml::node& node) -> std::string {
|
||||
if (node.is_string()) { return node.get_value<std::string>(); }
|
||||
if (node.is_integer()) { return std::to_string(node.get_value<int>()); }
|
||||
return "black";
|
||||
}
|
||||
|
||||
// Convierte string de autoSurface a int
|
||||
auto RoomLoader::convertAutoSurface(const fkyaml::node& node) -> int { // NOLINT(readability-convert-member-functions-to-static)
|
||||
if (node.is_integer()) {
|
||||
@@ -71,10 +78,10 @@ void RoomLoader::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, con
|
||||
room.name = room_node["name"].get_value<std::string>();
|
||||
}
|
||||
if (room_node.contains("bgColor")) {
|
||||
room.bg_color = room_node["bgColor"].get_value<std::string>();
|
||||
room.bg_color = readColorNode(room_node["bgColor"]);
|
||||
}
|
||||
if (room_node.contains("border")) {
|
||||
room.border_color = room_node["border"].get_value<std::string>();
|
||||
room.border_color = readColorNode(room_node["border"]);
|
||||
}
|
||||
if (room_node.contains("tileSetFile")) {
|
||||
room.tile_set_file = room_node["tileSetFile"].get_value<std::string>();
|
||||
@@ -87,11 +94,11 @@ void RoomLoader::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, con
|
||||
|
||||
// Item colors
|
||||
room.item_color1 = room_node.contains("itemColor1")
|
||||
? room_node["itemColor1"].get_value_or<std::string>("yellow")
|
||||
? readColorNode(room_node["itemColor1"])
|
||||
: "yellow";
|
||||
|
||||
room.item_color2 = room_node.contains("itemColor2")
|
||||
? room_node["itemColor2"].get_value_or<std::string>("magenta")
|
||||
? readColorNode(room_node["itemColor2"])
|
||||
: "magenta";
|
||||
|
||||
// Dirección de la cinta transportadora (left/none/right)
|
||||
|
||||
@@ -34,7 +34,7 @@ Credits::Credits()
|
||||
|
||||
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); // Cambia el color del borde
|
||||
fillTexture(); // Escribe el texto en la textura
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica
|
||||
}
|
||||
|
||||
// Comprueba el manejador de eventos
|
||||
|
||||
@@ -35,12 +35,12 @@ class GameOver {
|
||||
static constexpr float ENDING_DURATION = 1.12F; // Espera en negro antes de salir
|
||||
|
||||
// --- Constantes de posición ---
|
||||
static constexpr int TEXT_Y = 32; // Posición Y del texto principal
|
||||
static constexpr int SPRITE_Y_OFFSET = 30; // Offset Y para sprites desde TEXT_Y
|
||||
static constexpr int PLAYER_X_OFFSET = 10; // Offset X del jugador desde el centro
|
||||
static constexpr int TV_X_OFFSET = 10; // Offset X del TV desde el centro
|
||||
static constexpr int ITEMS_Y_OFFSET = 80; // Offset Y del texto de items desde TEXT_Y
|
||||
static constexpr int ROOMS_Y_OFFSET = 90; // Offset Y del texto de rooms desde TEXT_Y
|
||||
static constexpr int TEXT_Y = 32; // Posición Y del texto principal
|
||||
static constexpr int SPRITE_Y_OFFSET = 30; // Offset Y para sprites desde TEXT_Y
|
||||
static constexpr int PLAYER_X_OFFSET = 10; // Offset X del jugador desde el centro
|
||||
static constexpr int TV_X_OFFSET = 10; // Offset X del TV desde el centro
|
||||
static constexpr int ITEMS_Y_OFFSET = 80; // Offset Y del texto de items desde TEXT_Y
|
||||
static constexpr int ROOMS_Y_OFFSET = 90; // Offset Y del texto de rooms desde TEXT_Y
|
||||
|
||||
// --- Métodos ---
|
||||
void update(); // Actualiza el objeto
|
||||
|
||||
@@ -47,7 +47,7 @@ Title::Title()
|
||||
initMarquee(); // Inicializa la marquesina
|
||||
createCheevosTexture(); // Crea y rellena la textura para mostrar los logros
|
||||
Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK)); // Cambia el color del borde
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica
|
||||
Audio::get()->playMusic("574071_EA_DTV.ogg"); // Inicia la musica
|
||||
}
|
||||
|
||||
// Destructor
|
||||
|
||||
@@ -17,75 +17,75 @@
|
||||
* de 3 niveles de intensidad (0, 128, 255) para cada componente RGB.
|
||||
*/
|
||||
class Color {
|
||||
public:
|
||||
/**
|
||||
* @enum Cpc
|
||||
* @brief Índices de los colores de la paleta Amstrad CPC
|
||||
*
|
||||
* Los nombres corresponden a los colores oficiales documentados por Amstrad.
|
||||
* El índice 0 está reservado para transparencia.
|
||||
*/
|
||||
enum class Cpc : Uint8 {
|
||||
// Transparente (índice 0)
|
||||
CLEAR = 0, // Nota: No usar "TRANSPARENT" - colisiona con macro de Windows
|
||||
public:
|
||||
/**
|
||||
* @enum Cpc
|
||||
* @brief Índices de los colores de la paleta Amstrad CPC
|
||||
*
|
||||
* Los nombres corresponden a los colores oficiales documentados por Amstrad.
|
||||
* El índice 0 está reservado para transparencia.
|
||||
*/
|
||||
enum class Cpc : Uint8 {
|
||||
// Transparente (índice 0)
|
||||
CLEAR = 0, // Nota: No usar "TRANSPARENT" - colisiona con macro de Windows
|
||||
|
||||
// Negros y azules (R=0)
|
||||
BLACK = 1, // 0, 0, 0
|
||||
BLUE = 2, // 0, 0, 128
|
||||
BRIGHT_BLUE = 3, // 0, 0, 255
|
||||
// Negros y azules (R=0)
|
||||
BLACK = 1, // 0, 0, 0
|
||||
BLUE = 2, // 0, 0, 128
|
||||
BRIGHT_BLUE = 3, // 0, 0, 255
|
||||
|
||||
// Rojos y magentas (G=0)
|
||||
RED = 4, // 128, 0, 0
|
||||
MAGENTA = 5, // 128, 0, 128
|
||||
MAUVE = 6, // 128, 0, 255
|
||||
BRIGHT_RED = 7, // 255, 0, 0
|
||||
PURPLE = 8, // 255, 0, 128
|
||||
BRIGHT_MAGENTA = 9, // 255, 0, 255
|
||||
// Rojos y magentas (G=0)
|
||||
RED = 4, // 128, 0, 0
|
||||
MAGENTA = 5, // 128, 0, 128
|
||||
MAUVE = 6, // 128, 0, 255
|
||||
BRIGHT_RED = 7, // 255, 0, 0
|
||||
PURPLE = 8, // 255, 0, 128
|
||||
BRIGHT_MAGENTA = 9, // 255, 0, 255
|
||||
|
||||
// Verdes y cianes (R=0, G>0)
|
||||
GREEN = 10, // 0, 128, 0
|
||||
CYAN = 11, // 0, 128, 128
|
||||
SKY_BLUE = 12, // 0, 128, 255
|
||||
// Verdes y cianes (R=0, G>0)
|
||||
GREEN = 10, // 0, 128, 0
|
||||
CYAN = 11, // 0, 128, 128
|
||||
SKY_BLUE = 12, // 0, 128, 255
|
||||
|
||||
// Amarillos y blancos medios (G=128)
|
||||
YELLOW = 13, // 128, 128, 0
|
||||
WHITE = 14, // 128, 128, 128
|
||||
PASTEL_BLUE = 15, // 128, 128, 255
|
||||
// Amarillos y blancos medios (G=128)
|
||||
YELLOW = 13, // 128, 128, 0
|
||||
WHITE = 14, // 128, 128, 128
|
||||
PASTEL_BLUE = 15, // 128, 128, 255
|
||||
|
||||
// Naranjas y rosas (R=255, G=128)
|
||||
ORANGE = 16, // 255, 128, 0
|
||||
PINK = 17, // 255, 128, 128
|
||||
PASTEL_MAGENTA = 18, // 255, 128, 255
|
||||
// Naranjas y rosas (R=255, G=128)
|
||||
ORANGE = 16, // 255, 128, 0
|
||||
PINK = 17, // 255, 128, 128
|
||||
PASTEL_MAGENTA = 18, // 255, 128, 255
|
||||
|
||||
// Verdes brillantes (G=255)
|
||||
BRIGHT_GREEN = 19, // 0, 255, 0
|
||||
SEA_GREEN = 20, // 0, 255, 128
|
||||
BRIGHT_CYAN = 21, // 0, 255, 255
|
||||
// Verdes brillantes (G=255)
|
||||
BRIGHT_GREEN = 19, // 0, 255, 0
|
||||
SEA_GREEN = 20, // 0, 255, 128
|
||||
BRIGHT_CYAN = 21, // 0, 255, 255
|
||||
|
||||
// Limas y pasteles verdes (G=255)
|
||||
LIME = 22, // 128, 255, 0
|
||||
PASTEL_GREEN = 23, // 128, 255, 128
|
||||
PASTEL_CYAN = 24, // 128, 255, 255
|
||||
// Limas y pasteles verdes (G=255)
|
||||
LIME = 22, // 128, 255, 0
|
||||
PASTEL_GREEN = 23, // 128, 255, 128
|
||||
PASTEL_CYAN = 24, // 128, 255, 255
|
||||
|
||||
// Amarillos brillantes y blancos (R=255, G=255)
|
||||
BRIGHT_YELLOW = 25, // 255, 255, 0
|
||||
PASTEL_YELLOW = 26, // 255, 255, 128
|
||||
BRIGHT_WHITE = 27 // 255, 255, 255
|
||||
};
|
||||
// Amarillos brillantes y blancos (R=255, G=255)
|
||||
BRIGHT_YELLOW = 25, // 255, 255, 0
|
||||
PASTEL_YELLOW = 26, // 255, 255, 128
|
||||
BRIGHT_WHITE = 27 // 255, 255, 255
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Obtiene el índice de paleta de un color CPC
|
||||
* @param color Color del enum Cpc
|
||||
* @return Índice de paleta (Uint8)
|
||||
*/
|
||||
static constexpr auto index(Cpc color) -> Uint8 {
|
||||
return static_cast<Uint8>(color);
|
||||
}
|
||||
/**
|
||||
* @brief Obtiene el índice de paleta de un color CPC
|
||||
* @param color Color del enum Cpc
|
||||
* @return Índice de paleta (Uint8)
|
||||
*/
|
||||
static constexpr auto index(Cpc color) -> Uint8 {
|
||||
return static_cast<Uint8>(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convierte un nombre de color (string) a índice de paleta
|
||||
* @param name Nombre del color en minúsculas (ej: "cyan", "bright_blue")
|
||||
* @return Índice de paleta, o 1 (BLACK) si no se encuentra
|
||||
*/
|
||||
static auto fromString(const std::string& name) -> Uint8;
|
||||
/**
|
||||
* @brief Convierte un nombre de color (string) a índice de paleta
|
||||
* @param name Nombre del color en minúsculas (ej: "cyan", "bright_blue")
|
||||
* @return Índice de paleta, o 1 (BLACK) si no se encuentra
|
||||
*/
|
||||
static auto fromString(const std::string& name) -> Uint8;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace Texts {
|
||||
constexpr const char* WINDOW_CAPTION = "© 2026 Projecte 2026 — JailDesigner";
|
||||
constexpr const char* COPYRIGHT = "@2026 JailDesigner";
|
||||
constexpr const char* VERSION = "1.13"; // Versión por defecto
|
||||
constexpr const char* VERSION = "0.1"; // Versión por defecto
|
||||
} // namespace Texts
|
||||
|
||||
// Tamaño de bloque
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
#include <algorithm> // Para find, transform
|
||||
#include <cctype> // Para tolower
|
||||
#include <algorithm> // Para find, transform, ranges::all_of
|
||||
#include <array> // Para array
|
||||
#include <cctype> // Para tolower, isdigit
|
||||
#include <cmath> // Para round, abs
|
||||
#include <cstdlib> // Para abs
|
||||
#include <exception> // Para exception
|
||||
@@ -366,10 +367,36 @@ auto stringToColor(const std::string& str) -> Uint8 {
|
||||
auto it = PALETTE_MAP.find(str);
|
||||
if (it != PALETTE_MAP.end()) {
|
||||
return it->second;
|
||||
} // Si no se encuentra el color, devolvemos negro por defecto
|
||||
}
|
||||
|
||||
// Fallback: si el string es numérico (p.ej. "4"), lo tratamos como índice de paleta
|
||||
if (!str.empty() && std::ranges::all_of(str, [](char c) { return std::isdigit(static_cast<unsigned char>(c)) != 0; })) {
|
||||
const int IDX = safeStoi(str, 0);
|
||||
if (IDX >= 0 && IDX <= 255) {
|
||||
return static_cast<Uint8>(IDX);
|
||||
}
|
||||
}
|
||||
|
||||
// Si no se encuentra el color, devolvemos negro por defecto
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Inverso de stringToColor: devuelve el nombre canónico para un índice de paleta (o el propio número si no hay)
|
||||
auto colorToString(Uint8 index) -> std::string {
|
||||
static const std::array<const char*, 16> NAMES = {
|
||||
"black", "bright_black",
|
||||
"blue", "bright_blue",
|
||||
"red", "bright_red",
|
||||
"magenta", "bright_magenta",
|
||||
"green", "bright_green",
|
||||
"cyan", "bright_cyan",
|
||||
"yellow", "bright_yellow",
|
||||
"white", "bright_white"};
|
||||
if (index < NAMES.size()) { return NAMES[index]; }
|
||||
if (index == 255) { return "transparent"; }
|
||||
return std::to_string(index);
|
||||
}
|
||||
|
||||
// Convierte una cadena a un entero de forma segura
|
||||
auto safeStoi(const std::string& value, int default_value) -> int {
|
||||
try {
|
||||
|
||||
@@ -92,7 +92,8 @@ auto toSDLRect(const SDL_FRect& frect) -> SDL_Rect; // Convierte SDL_FRect
|
||||
auto toSDLPoint(const SDL_FPoint& fpoint) -> SDL_Point; // Convierte SDL_FPoint a SDL_Point
|
||||
|
||||
// CONVERSIONES DE STRING
|
||||
auto stringToColor(const std::string& str) -> Uint8; // String a índice de paleta
|
||||
auto stringToColor(const std::string& str) -> Uint8; // String a índice de paleta (acepta nombres o números)
|
||||
auto colorToString(Uint8 index) -> std::string; // Índice de paleta a nombre canónico
|
||||
auto safeStoi(const std::string& value, int default_value = 0) -> int; // String a int seguro (sin excepciones)
|
||||
auto stringToBool(const std::string& str) -> bool; // String a bool (true/1/yes/on)
|
||||
auto boolToString(bool value) -> std::string; // Bool a string (1/0)
|
||||
|
||||
Reference in New Issue
Block a user