From b37b62ef1ec0e60a8691e6965b99f3dd8f11b446 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 1 Apr 2026 21:31:25 +0200 Subject: [PATCH] =?UTF-8?q?-=20pots=20canviar=20el=20color=20del=20jugador?= =?UTF-8?q?=20desde=20la=20consola=20(persistent)=20-=20cokmprova=20que=20?= =?UTF-8?q?el=20color=20no=20siga=20el=20mateix=20que=20el=20del=20fono=20?= =?UTF-8?q?(canvia=20a=20default)=20-=20eliminades=20animacions=20sobrants?= =?UTF-8?q?=20del=20jugador=20-=20canviada=20la=20logica=20del=20marcador?= =?UTF-8?q?=20pero=20a=20mostrar=20la=20animaci=C3=B3=20de=20les=20vides?= =?UTF-8?q?=20del=20jugador=20-=20posibilitat=20d'utilitzar=20skins=20d'en?= =?UTF-8?q?emics=20en=20el=20jugador=20-=20canvi=20en=20calent=20de=20la?= =?UTF-8?q?=20skin=20en=20el=20marcador=20(abans=20soles=20en=20el=20const?= =?UTF-8?q?ructir)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/console/commands.yaml | 27 +++++++++------- data/player/player.yaml | 12 +------ data/player/player2.yaml | 12 +------ source/game/defaults.hpp | 3 +- source/game/entities/player.cpp | 30 ++++++++++++++---- source/game/entities/player.hpp | 3 +- source/game/game_control.hpp | 6 ++-- source/game/gameplay/scoreboard.cpp | 22 ++++++++----- source/game/gameplay/scoreboard.hpp | 2 +- source/game/options.cpp | 14 +++++++-- source/game/options.hpp | 3 +- source/game/scenes/ending2.cpp | 2 +- source/game/scenes/game.cpp | 17 +++++++--- source/game/ui/console_commands.cpp | 49 +++++++++++++++++++++-------- 14 files changed, 128 insertions(+), 74 deletions(-) diff --git a/data/console/commands.yaml b/data/console/commands.yaml index d00f710..ac78da5 100644 --- a/data/console/commands.yaml +++ b/data/console/commands.yaml @@ -108,21 +108,24 @@ categories: - name: GAME commands: + - keyword: PLAYER + handler: cmd_player + description: "Player skin and color" + usage: "PLAYER SKIN | PLAYER COLOR <0-15>|DEFAULT" + completions: + PLAYER: [SKIN, COLOR] + PLAYER SKIN: [DEFAULT, ABAD, BATMAN, CHIP, CONGO, JEANNINE, MUMMY, UPV_STUDENT] + PLAYER COLOR: [DEFAULT] + - keyword: SET handler: cmd_set - description: "Change player skin" - usage: "SET PLAYER SKIN <1|2>" + description: "Set debug options" + usage: "SET INITIAL [ROOM|POS|SCENE] | SET ITEMS <0-200>" + debug_only: true completions: - SET: [PLAYER] - SET PLAYER: [SKIN] - debug_extras: - description: "Set player/debug options" - usage: "SET PLAYER SKIN <1|2> | SET INITIAL [ROOM|POS|SCENE] | SET ITEMS <0-200>" - completions: - SET: [PLAYER, INITIAL, ITEMS] - SET PLAYER: [SKIN] - SET INITIAL: [ROOM, POS, SCENE] - SET INITIAL SCENE: [LOGO, LOADING, TITLE, CREDITS, GAME, ENDING, ENDING2] + SET: [INITIAL, ITEMS] + SET INITIAL: [ROOM, POS, SCENE] + SET INITIAL SCENE: [LOGO, LOADING, TITLE, CREDITS, GAME, ENDING, ENDING2] - keyword: RESTART handler: cmd_restart diff --git a/data/player/player.yaml b/data/player/player.yaml index e35efa4..d8fa865 100644 --- a/data/player/player.yaml +++ b/data/player/player.yaml @@ -4,17 +4,7 @@ frameWidth: 8 frameHeight: 16 animations: - - name: stand - speed: 0.1333 - loop: 0 - frames: [0] - - - name: walk + - name: default speed: 0.1333 loop: 0 frames: [0, 1, 2, 3] - - - name: walk_menu - speed: 0.0 - loop: 0 - frames: [0, 1, 2, 3] diff --git a/data/player/player2.yaml b/data/player/player2.yaml index 395ab89..523b248 100644 --- a/data/player/player2.yaml +++ b/data/player/player2.yaml @@ -4,17 +4,7 @@ frameWidth: 8 frameHeight: 16 animations: - - name: stand - speed: 0.1333 - loop: 0 - frames: [0] - - - name: walk + - name: default speed: 0.1333 loop: 0 frames: [0, 1, 2, 3, 4, 5, 6, 7] - - - name: walk_menu - speed: 0.0 - loop: 0 - frames: [0, 1, 2, 3, 4, 5, 6, 7] diff --git a/source/game/defaults.hpp b/source/game/defaults.hpp index c3f996f..49bb99b 100644 --- a/source/game/defaults.hpp +++ b/source/game/defaults.hpp @@ -101,5 +101,6 @@ namespace Defaults::Game::Player { constexpr int SPAWN_X = 25 * Tile::SIZE; // Posición X inicial constexpr int SPAWN_Y = 13 * Tile::SIZE; // Posición Y inicial constexpr SDL_FlipMode SPAWN_FLIP = Flip::LEFT; // Orientación inicial - constexpr int SKIN = 1; // Skin del jugador por defecto (1=normal, 2=alternativa) + constexpr const char* SKIN = "default"; // Skin del jugador por defecto + constexpr int COLOR = -1; // Color del jugador (-1 = automático según cheats) } // namespace Defaults::Game::Player diff --git a/source/game/entities/player.cpp b/source/game/entities/player.cpp index fe07220..15f5107 100644 --- a/source/game/entities/player.cpp +++ b/source/game/entities/player.cpp @@ -620,20 +620,31 @@ auto Player::handleKillingTiles() -> bool { return false; // No se encontró ninguna colisión } -// Establece el color del jugador (0 = automático según cheats) +// Establece el color del jugador (0 = automático según cheats/options) void Player::setColor(Uint8 color) { if (color != 0) { color_ = color; return; } - if (Options::cheats.invincible == Options::Cheat::State::ENABLED) { + // Color personalizado desde opciones (prioridad sobre cheats) + if (Options::game.player_color >= 0) { + color_ = static_cast(Options::game.player_color); + } else if (Options::cheats.invincible == Options::Cheat::State::ENABLED) { + // Color automático según cheats color_ = static_cast(PaletteColor::CYAN); } else if (Options::cheats.infinite_lives == Options::Cheat::State::ENABLED) { color_ = static_cast(PaletteColor::YELLOW); } else { color_ = static_cast(PaletteColor::WHITE); } + + // Si el color coincide con el fondo de la habitación, usar fallback + if (room_ != nullptr && color_ == room_->getBGColor()) { + color_ = (room_->getBGColor() != static_cast(PaletteColor::WHITE)) + ? static_cast(PaletteColor::WHITE) + : static_cast(PaletteColor::BRIGHT_BLACK); + } } // Actualiza los puntos de colisión @@ -765,11 +776,18 @@ void Player::applySpawnValues(const SpawnData& spawn) { sprite_->setFlip(spawn.flip); } +// Resuelve nombre de skin a fichero de animación +auto Player::skinToAnimationPath(const std::string& skin_name) -> std::string { + if (skin_name == "default") { + return "player.yaml"; + } + return skin_name + ".yaml"; +} + // Cambia la skin del jugador en caliente preservando la orientación actual -void Player::setSkin(int skin_num) { +void Player::setSkin(const std::string& skin_name) { const auto FLIP = sprite_->getFlip(); - const std::string PATH = (skin_num == 2) ? "player2.yaml" : "player.yaml"; - initSprite(PATH); + initSprite(skinToAnimationPath(skin_name)); sprite_->setFlip(FLIP); } @@ -779,7 +797,7 @@ void Player::initSprite(const std::string& animations_path) { // NOLINT(readabi sprite_ = std::make_unique(animation_data); sprite_->setWidth(WIDTH); sprite_->setHeight(HEIGHT); - sprite_->setCurrentAnimation("walk"); + sprite_->setCurrentAnimation("default"); } // Actualiza la posición del sprite y las colisiones diff --git a/source/game/entities/player.hpp b/source/game/entities/player.hpp index bd9d685..a26c773 100644 --- a/source/game/entities/player.hpp +++ b/source/game/entities/player.hpp @@ -100,7 +100,8 @@ class Player { auto getCollider() -> SDL_FRect& { return collider_box_; } // Obtiene el rectangulo de colision del jugador auto getSpawnParams() -> SpawnData { return {.x = x_, .y = y_, .vx = vx_, .vy = vy_, .last_grounded_position = last_grounded_position_, .state = state_, .flip = sprite_->getFlip()}; } // Obtiene el estado de reaparición del jugador void setColor(Uint8 color = 0); // Establece el color del jugador (0 = automático según cheats) - void setSkin(int skin_num); // Cambia la skin del jugador en caliente (1=normal, 2=alternativa) + void setSkin(const std::string& skin_name); // Cambia la skin del jugador en caliente ("default" o nombre de enemigo) + static auto skinToAnimationPath(const std::string& skin_name) -> std::string; // Resuelve nombre de skin a fichero de animación void setRoom(std::shared_ptr room) { room_ = std::move(room); } // Establece la habitación en la que se encuentra el jugador //[[nodiscard]] auto isAlive() const -> bool { return is_alive_ || (Options::cheats.invincible == Options::Cheat::State::ENABLED); } // Comprueba si el jugador esta vivo [[nodiscard]] auto isAlive() const -> bool { return is_alive_; } // Comprueba si el jugador esta vivo diff --git a/source/game/game_control.hpp b/source/game/game_control.hpp index 1334dd9..a6a88fb 100644 --- a/source/game/game_control.hpp +++ b/source/game/game_control.hpp @@ -5,8 +5,10 @@ namespace GameControl { // Disponible en todos los builds — refresca el color del jugador según cheats inline std::function refresh_player_color; - // Disponible en todos los builds — cambia la skin del jugador (1=normal, 2=alternativa) - inline std::function change_player_skin; + // Disponible en todos los builds — cambia la skin del jugador ("default" o nombre de enemigo) + inline std::function change_player_skin; + // Disponible en todos los builds — cambia el color del jugador (-1 = automático, 0-15 = color fijo) + inline std::function change_player_color; } // namespace GameControl #ifdef _DEBUG diff --git a/source/game/gameplay/scoreboard.cpp b/source/game/gameplay/scoreboard.cpp index 6e913b1..c18720d 100644 --- a/source/game/gameplay/scoreboard.cpp +++ b/source/game/gameplay/scoreboard.cpp @@ -10,6 +10,7 @@ #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/text.hpp" // Para Text #include "core/resources/resource_cache.hpp" // Para Resource +#include "game/entities/player.hpp" // Para Player::skinToAnimationPath #include "game/options.hpp" // Para Options, options, Cheat, OptionsGame #include "utils/defines.hpp" // Para BLOCK #include "utils/utils.hpp" // Para stringToColor @@ -22,9 +23,10 @@ Scoreboard::Scoreboard(std::shared_ptr data) constexpr float SURFACE_HEIGHT = 6.0F * Tile::SIZE; // Reserva memoria para los objetos - const auto& player_animation_data = Resource::Cache::get()->getAnimationData((Options::game.player_skin == 2) ? "player2.yaml" : "player.yaml"); + const std::string player_anim_path = Player::skinToAnimationPath(Options::game.player_skin); + const auto& player_animation_data = Resource::Cache::get()->getAnimationData(player_anim_path); player_sprite_ = std::make_shared(player_animation_data); - player_sprite_->setCurrentAnimation("walk_menu"); + player_sprite_->setCurrentAnimation("default"); surface_ = std::make_shared(SURFACE_WIDTH, SURFACE_HEIGHT); surface_dest_ = {.x = 0, .y = Options::game.height - SURFACE_HEIGHT, .w = SURFACE_WIDTH, .h = SURFACE_HEIGHT}; @@ -49,9 +51,6 @@ void Scoreboard::update(float delta_time) { // Acumular tiempo para animaciones time_accumulator_ += delta_time; - // Actualizar sprite con delta time - player_sprite_->update(delta_time); - // Actualiza el color de la cantidad de items recogidos updateItemsColor(delta_time); @@ -77,6 +76,14 @@ auto Scoreboard::getTime() -> Scoreboard::ClockData { // NOLINT(readability-con return time; } +// Actualiza el sprite del jugador con la skin actual +void Scoreboard::refreshPlayerSkin() { + const std::string player_anim_path = Player::skinToAnimationPath(Options::game.player_skin); + const auto& player_animation_data = Resource::Cache::get()->getAnimationData(player_anim_path); + player_sprite_ = std::make_shared(player_animation_data); + player_sprite_->setCurrentAnimation("default"); +} + // Pone el marcador en modo pausa void Scoreboard::setPaused(bool value) { if (is_paused_ == value) { @@ -136,8 +143,9 @@ void Scoreboard::fillTexture() { // Dibuja las vidas // Calcular desplazamiento basado en tiempo - const int DESP = static_cast(time_accumulator_ / SPRITE_WALK_CYCLE_DURATION) % 8; - const int FRAME = DESP % SPRITE_WALK_FRAMES; + const int WALK_FRAMES = player_sprite_->getCurrentAnimationSize(); + const int DESP = static_cast(time_accumulator_ / SPRITE_WALK_CYCLE_DURATION) % (WALK_FRAMES * 2); + const int FRAME = DESP % WALK_FRAMES; player_sprite_->setCurrentAnimationFrame(FRAME); player_sprite_->setPosY(LINE2); for (int i = 0; i < data_->lives; ++i) { diff --git a/source/game/gameplay/scoreboard.hpp b/source/game/gameplay/scoreboard.hpp index d6e63c1..0277030 100644 --- a/source/game/gameplay/scoreboard.hpp +++ b/source/game/gameplay/scoreboard.hpp @@ -28,6 +28,7 @@ class Scoreboard { void render(); // Pinta el objeto en pantalla void update(float delta_time); // Actualiza las variables del objeto void setPaused(bool value); // Pone el marcador en modo pausa + void refreshPlayerSkin(); // Actualiza el sprite del jugador con la skin actual auto getMinutes() -> int; // Devuelve la cantidad de minutos de juego transcurridos private: @@ -42,7 +43,6 @@ class Scoreboard { // Constantes de tiempo static constexpr float ITEMS_COLOR_BLINK_DURATION = 0.333F; // Duración de cada estado del parpadeo (era 10 frames @ 60fps) static constexpr float SPRITE_WALK_CYCLE_DURATION = 0.667F; // Duración del ciclo de caminar (era 40 frames @ 60fps) - static constexpr int SPRITE_WALK_FRAMES = 4; // Número de frames de animación // Métodos privados auto getTime() -> ClockData; // Obtiene el tiempo transcurrido de partida diff --git a/source/game/options.cpp b/source/game/options.cpp index fcd28cf..b79ab70 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -510,12 +510,19 @@ namespace Options { const auto& player_node = yaml["player"]; if (player_node.contains("skin")) { try { - int skin = player_node["skin"].get_value(); - game.player_skin = (skin == 2) ? 2 : Defaults::Game::Player::SKIN; + game.player_skin = player_node["skin"].get_value(); } catch (...) { game.player_skin = Defaults::Game::Player::SKIN; } } + if (player_node.contains("color")) { + try { + int color = player_node["color"].get_value(); + game.player_color = (color >= 0 && color <= 15) ? color : Defaults::Game::Player::COLOR; + } catch (...) { + game.player_color = Defaults::Game::Player::COLOR; + } + } } } @@ -778,7 +785,8 @@ namespace Options { file << "\n"; file << "# PLAYER\n"; file << "player:\n"; - file << " skin: " << game.player_skin << "\n"; + file << " skin: \"" << game.player_skin << "\"\n"; + file << " color: " << game.player_color << "\n"; file << "\n"; file << "# KIOSK MODE\n"; diff --git a/source/game/options.hpp b/source/game/options.hpp index 708de4b..87a8087 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -137,7 +137,8 @@ namespace Options { struct Game { float width{Defaults::Canvas::WIDTH}; // Ancho de la resolucion del juego float height{Defaults::Canvas::HEIGHT}; // Alto de la resolucion del juego - int player_skin{Defaults::Game::Player::SKIN}; // Skin del jugador (1=normal, 2=alternativa) + std::string player_skin{Defaults::Game::Player::SKIN}; // Skin del jugador ("default" o nombre de enemigo) + int player_color{Defaults::Game::Player::COLOR}; // Color del jugador (-1 = automático, 0-15 = color fijo) }; // Estructura para un preset de PostFX diff --git a/source/game/scenes/ending2.cpp b/source/game/scenes/ending2.cpp index 44c3338..888a518 100644 --- a/source/game/scenes/ending2.cpp +++ b/source/game/scenes/ending2.cpp @@ -391,7 +391,7 @@ void Ending2::placeSprites() const { const float X = (Options::game.width - sprites_.back()->getWidth()) / 2; const float Y = sprites_.back()->getPosY() + (sprite_max_height_ * 2); sprites_.back()->setPos(X, Y); - sprites_.back()->setCurrentAnimation("walk"); + sprites_.back()->setCurrentAnimation("default"); } // Crea los sprites con las texturas con los textos diff --git a/source/game/scenes/game.cpp b/source/game/scenes/game.cpp index 4227402..6167920 100644 --- a/source/game/scenes/game.cpp +++ b/source/game/scenes/game.cpp @@ -67,9 +67,14 @@ Game::Game(Mode mode) GameControl::refresh_player_color = [this]() -> void { player_->setColor(); }; Console::get()->on_toggle = [this](bool open) { player_->setIgnoreInput(open); }; if (Console::get()->isActive()) { player_->setIgnoreInput(true); } - GameControl::change_player_skin = [this](int skin_num) -> void { - Options::game.player_skin = skin_num; - player_->setSkin(skin_num); + GameControl::change_player_skin = [this](const std::string& skin_name) -> void { + Options::game.player_skin = skin_name; + player_->setSkin(skin_name); + scoreboard_->refreshPlayerSkin(); + }; + GameControl::change_player_color = [this](int color) -> void { + Options::game.player_color = color; + player_->setColor(); }; #ifdef _DEBUG @@ -126,6 +131,7 @@ Game::~Game() { GameControl::refresh_player_color = nullptr; GameControl::change_player_skin = nullptr; + GameControl::change_player_color = nullptr; Console::get()->on_toggle = nullptr; #ifdef _DEBUG @@ -655,6 +661,9 @@ auto Game::changeRoom(const std::string& room_path) -> bool { // Pasa la nueva habitación al jugador player_->setRoom(room_); + // Recalcula el color del jugador (evita coincidir con el fondo) + player_->setColor(); + // Cambia la habitación actual current_room_ = room_path; @@ -901,7 +910,7 @@ void Game::checkEndGameCheevos() { // NOLINT(readability-convert-member-functio // Inicializa al jugador void Game::initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr room) { // NOLINT(readability-convert-member-functions-to-static) const bool IGNORE_INPUT = player_ != nullptr && player_->getIgnoreInput(); - std::string player_animations = (Options::game.player_skin == 2) ? "player2.yaml" : "player.yaml"; + std::string player_animations = Player::skinToAnimationPath(Options::game.player_skin); const Player::Data PLAYER{.spawn_data = spawn_point, .animations_path = player_animations, .room = std::move(room)}; player_ = std::make_shared(PLAYER); if (IGNORE_INPUT) { player_->setIgnoreInput(true); } diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index 19cbc7c..24c07c1 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -623,19 +623,41 @@ static auto cmd_cheat(const std::vector& args) -> std::string { return "usage: cheat [infinite lives|invincibility|open the jail|close the jail]"; } -// SET PLAYER SKIN / SET INITIAL / SET ITEMS -static auto cmd_set(const std::vector& args) -> std::string { - if (args.size() >= 3 && args[0] == "PLAYER" && args[1] == "SKIN") { - if (SceneManager::current != SceneManager::Scene::GAME) { return "Only available in GAME scene"; } - int num = 0; - try { - num = std::stoi(args[2]); - } catch (...) {} - if (num < 1 || num > 2) { return "usage: set player skin <1|2>"; } +// PLAYER SKIN / PLAYER COLOR +static auto cmd_player(const std::vector& args) -> std::string { + if (SceneManager::current != SceneManager::Scene::GAME) { return "Only available in GAME scene"; } + + // PLAYER SKIN + if (args.size() >= 2 && args[0] == "SKIN") { if (!GameControl::change_player_skin) { return "Game not initialized"; } - GameControl::change_player_skin(num); - return "Player skin: " + std::to_string(num); + std::string skin_name = args[1]; + std::ranges::transform(skin_name, skin_name.begin(), ::tolower); + GameControl::change_player_skin(skin_name); + return "Player skin: " + skin_name; } + + // PLAYER COLOR DEFAULT + if (args.size() >= 2 && args[0] == "COLOR" && args[1] == "DEFAULT") { + if (!GameControl::change_player_color) { return "Game not initialized"; } + GameControl::change_player_color(-1); + return "Player color: default"; + } + + // PLAYER COLOR <0-15> + if (args.size() >= 2 && args[0] == "COLOR") { + int color = -1; + try { color = std::stoi(args[1]); } catch (...) {} + if (color < 0 || color > 15) { return "usage: player color <0-15>|default"; } + if (!GameControl::change_player_color) { return "Game not initialized"; } + GameControl::change_player_color(color); + return "Player color: " + std::to_string(color); + } + + return "usage: player skin | player color <0-15>|default"; +} + +// SET INITIAL / SET ITEMS +static auto cmd_set(const std::vector& args) -> std::string { #ifdef _DEBUG // SET INITIAL SCENE [] if (args.size() >= 2 && args[0] == "INITIAL" && args[1] == "SCENE") { @@ -689,7 +711,7 @@ static auto cmd_set(const std::vector& args) -> std::string { return "Items: " + std::to_string(count); } - if (args.empty() || args[0] != "INITIAL") { return "usage: set initial [room|pos|scene] | set items <0-200> | set player skin <1|2>"; } + if (args.empty() || args[0] != "INITIAL") { return "usage: set initial [room|pos|scene] | set items <0-200>"; } const bool DO_ROOM = args.size() == 1 || (args.size() >= 2 && args[1] == "ROOM"); const bool DO_POS = args.size() == 1 || (args.size() >= 2 && args[1] == "POS"); @@ -705,7 +727,7 @@ static auto cmd_set(const std::vector& args) -> std::string { } return result; #else - return "usage: set player skin <1|2>"; + return "Debug only command"; #endif } @@ -771,6 +793,7 @@ void CommandRegistry::registerHandlers() { handlers_["cmd_show"] = cmd_show; handlers_["cmd_hide"] = cmd_hide; handlers_["cmd_cheat"] = cmd_cheat; + handlers_["cmd_player"] = cmd_player; handlers_["cmd_set"] = cmd_set; handlers_["cmd_restart"] = cmd_restart; handlers_["cmd_kiosk"] = cmd_kiosk;