From d9ada12ee0d183c02d054321b027dbba57914474 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Thu, 19 Mar 2026 11:19:38 +0100 Subject: [PATCH] afegit surface_dissolve_sprite ending2 amb els fades correctes --- .../rendering/surface_animated_sprite.cpp | 10 +++ .../rendering/surface_animated_sprite.hpp | 3 + .../rendering/surface_dissolve_sprite.cpp | 26 +++++- .../rendering/surface_dissolve_sprite.hpp | 6 ++ source/game/scenes/ending2.cpp | 86 ++++++++++++++++--- source/game/scenes/ending2.hpp | 14 +-- 6 files changed, 124 insertions(+), 21 deletions(-) diff --git a/source/core/rendering/surface_animated_sprite.cpp b/source/core/rendering/surface_animated_sprite.cpp index 9fb95ab..53e589a 100644 --- a/source/core/rendering/surface_animated_sprite.cpp +++ b/source/core/rendering/surface_animated_sprite.cpp @@ -214,6 +214,15 @@ SurfaceAnimatedSprite::SurfaceAnimatedSprite(const AnimationResource& cached_dat } } +// Constructor per a subclasses amb surface directa (sense YAML) +SurfaceAnimatedSprite::SurfaceAnimatedSprite(std::shared_ptr surface, SDL_FRect pos) + : SurfaceMovingSprite(std::move(surface), pos) { + // animations_ queda buit (protegit per el guard de animate()) + if (surface_) { + clip_ = {0, 0, static_cast(surface_->getWidth()), static_cast(surface_->getHeight())}; + } +} + // Obtiene el indice de la animación a partir del nombre auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int { auto index = -1; @@ -230,6 +239,7 @@ auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int { // Calcula el frame correspondiente a la animación (time-based) void SurfaceAnimatedSprite::animate(float delta_time) { + if (animations_.empty()) return; if (animations_[current_animation_].speed <= 0.0F) { return; } diff --git a/source/core/rendering/surface_animated_sprite.hpp b/source/core/rendering/surface_animated_sprite.hpp index 887a2bb..e89b9f5 100644 --- a/source/core/rendering/surface_animated_sprite.hpp +++ b/source/core/rendering/surface_animated_sprite.hpp @@ -49,6 +49,9 @@ class SurfaceAnimatedSprite : public SurfaceMovingSprite { void setCurrentAnimationFrame(int num); // Establece el frame actual de la animación protected: + // Constructor per a ús de subclasses que gestionen la surface directament (sense YAML) + SurfaceAnimatedSprite(std::shared_ptr surface, SDL_FRect pos); + // Métodos protegidos void animate(float delta_time); // Calcula el frame correspondiente a la animación actual (time-based) diff --git a/source/core/rendering/surface_dissolve_sprite.cpp b/source/core/rendering/surface_dissolve_sprite.cpp index e05d22d..7b6b2ab 100644 --- a/source/core/rendering/surface_dissolve_sprite.cpp +++ b/source/core/rendering/surface_dissolve_sprite.cpp @@ -32,6 +32,18 @@ auto SurfaceDissolveSprite::computePixelRank(int col, int row, int frame_h, return y_factor * 0.7F + RANDOM * 0.3F; } +// Constructor per a surface directa (sense AnimationResource) +SurfaceDissolveSprite::SurfaceDissolveSprite(std::shared_ptr surface, SDL_FRect pos) + : SurfaceAnimatedSprite(std::move(surface), pos) { + if (surface_) { + const int W = static_cast(surface_->getWidth()); + const int H = static_cast(surface_->getHeight()); + surface_display_ = std::make_shared(W, H); + surface_display_->setTransparentColor(surface_->getTransparentColor()); + surface_display_->clear(surface_->getTransparentColor()); + } +} + // Constructor SurfaceDissolveSprite::SurfaceDissolveSprite(const AnimationResource& data) : SurfaceAnimatedSprite(data) { @@ -87,7 +99,8 @@ void SurfaceDissolveSprite::rebuildDisplaySurface() { } const float RANK = computePixelRank(col, row, SH, direction_); if (RANK >= progress_) { - dst_data->data[(SY + row) * DST_W + (SX + col)] = COLOR; + const Uint8 OUT = (COLOR == source_color_) ? target_color_ : COLOR; + dst_data->data[(SY + row) * DST_W + (SX + col)] = OUT; } } } @@ -123,9 +136,9 @@ void SurfaceDissolveSprite::update(float delta_time) { } } -// Renderitza: usa surface_display_ si hi ha dissolució activa +// Renderitza: usa surface_display_ (amb color replace) si disponible void SurfaceDissolveSprite::render() { - if (progress_ <= 0.0F || !surface_display_) { + if (!surface_display_) { SurfaceAnimatedSprite::render(); return; } @@ -167,3 +180,10 @@ void SurfaceDissolveSprite::stopTransition() { auto SurfaceDissolveSprite::isTransitionDone() const -> bool { return transition_mode_ == TransitionMode::NONE; } + +// Configura substitució de color per a la reconstrucció +void SurfaceDissolveSprite::setColorReplace(Uint8 source, Uint8 target) { + source_color_ = source; + target_color_ = target; + needs_rebuild_ = true; +} diff --git a/source/core/rendering/surface_dissolve_sprite.hpp b/source/core/rendering/surface_dissolve_sprite.hpp index 48b65ea..5856905 100644 --- a/source/core/rendering/surface_dissolve_sprite.hpp +++ b/source/core/rendering/surface_dissolve_sprite.hpp @@ -16,6 +16,7 @@ enum class DissolveDirection { NONE, DOWN, UP }; class SurfaceDissolveSprite : public SurfaceAnimatedSprite { public: explicit SurfaceDissolveSprite(const AnimationResource& data); + SurfaceDissolveSprite(std::shared_ptr surface, SDL_FRect pos); ~SurfaceDissolveSprite() override = default; void update(float delta_time) override; @@ -34,6 +35,9 @@ class SurfaceDissolveSprite : public SurfaceAnimatedSprite { void stopTransition(); [[nodiscard]] auto isTransitionDone() const -> bool; + // Substitució de color: en reconstruir, substitueix source per target + void setColorReplace(Uint8 source, Uint8 target); + private: enum class TransitionMode { NONE, DISSOLVING, GENERATING }; @@ -46,6 +50,8 @@ class SurfaceDissolveSprite : public SurfaceAnimatedSprite { float transition_elapsed_{0.0F}; SDL_FRect prev_clip_{0, 0, 0, 0}; bool needs_rebuild_{false}; + Uint8 source_color_{255}; // 255 = transparent = sense replace per defecte + Uint8 target_color_{0}; void rebuildDisplaySurface(); [[nodiscard]] static auto computePixelRank(int col, int row, int frame_h, diff --git a/source/game/scenes/ending2.cpp b/source/game/scenes/ending2.cpp index 908387d..9f9d0d5 100644 --- a/source/game/scenes/ending2.cpp +++ b/source/game/scenes/ending2.cpp @@ -9,7 +9,7 @@ #include "core/input/input.hpp" // Para Input #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface -#include "core/rendering/surface_animated_sprite.hpp" // Para SAnimatedSprite +#include "core/rendering/surface_dissolve_sprite.hpp" // Para SurfaceDissolveSprite #include "core/rendering/surface_moving_sprite.hpp" // Para SMovingSprite #include "core/rendering/text.hpp" // Para Text #include "core/resources/resource_cache.hpp" // Para Resource @@ -267,16 +267,40 @@ void Ending2::loadSprites() { // Carga los sprites for (const auto& file : sprite_list_) { const auto& animation_data = Resource::Cache::get()->getAnimationData(file + ".yaml"); - sprites_.emplace_back(std::make_shared(animation_data)); + sprites_.emplace_back(std::make_shared(animation_data)); + sprites_.back()->setColorReplace(1, static_cast(PaletteColor::RED)); + sprites_.back()->setProgress(1.0F); // comença invisible sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_); sprite_max_height_ = std::max(sprites_.back()->getHeight(), sprite_max_height_); } + + // El último sprite (player) va en blanco, no en rojo + sprites_.back()->setColorReplace(1, static_cast(PaletteColor::WHITE)); } // Actualiza los sprites void Ending2::updateSprites(float delta) { for (const auto& sprite : sprites_) { sprite->update(delta); + + const float Y = sprite->getPosY(); + const float H = sprite->getHeight(); + const float CANVAS_H = static_cast(Options::game.height); + + // Checkpoint inferior: sprite entra per baix → generar de dalt a baix + if (Y > static_cast(ENTRY_EXIT_PADDING) + && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING + && sprite->getProgress() >= 1.0F + && sprite->isTransitionDone()) { + sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP); + } + + // Checkpoint superior: sprite surt per dalt → dissoldre de dalt a baix + if (Y <= static_cast(ENTRY_EXIT_PADDING) + && sprite->getProgress() <= 0.0F + && sprite->isTransitionDone()) { + sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN); + } } } @@ -284,6 +308,23 @@ void Ending2::updateSprites(float delta) { void Ending2::updateTextSprites(float delta) { for (const auto& sprite : sprite_texts_) { sprite->update(delta); + + const float Y = sprite->getPosY(); + const float H = sprite->getHeight(); + const float CANVAS_H = static_cast(Options::game.height); + + if (Y > static_cast(ENTRY_EXIT_PADDING) + && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING + && sprite->getProgress() >= 1.0F + && sprite->isTransitionDone()) { + sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP); + } + + if (Y <= static_cast(ENTRY_EXIT_PADDING) + && sprite->getProgress() <= 0.0F + && sprite->isTransitionDone()) { + sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN); + } } } @@ -291,31 +332,46 @@ void Ending2::updateTextSprites(float delta) { void Ending2::updateTexts(float delta) { for (const auto& sprite : texts_) { sprite->update(delta); + + const float Y = sprite->getPosY(); + const float H = sprite->getHeight(); + const float CANVAS_H = static_cast(Options::game.height); + + // Checkpoint inferior: text entra per baix → generar de dalt a baix + if (Y > static_cast(ENTRY_EXIT_PADDING) + && Y <= CANVAS_H - H - ENTRY_EXIT_PADDING + && sprite->getProgress() >= 1.0F + && sprite->isTransitionDone()) { + sprite->startGenerate(TRANSITION_DURATION_MS, DissolveDirection::UP); + } + + // Checkpoint superior: text surt per dalt → dissoldre de dalt a baix + if (Y <= static_cast(ENTRY_EXIT_PADDING) + && sprite->getProgress() <= 0.0F + && sprite->isTransitionDone()) { + sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN); + } } } // Dibuja los sprites void Ending2::renderSprites() { - for (size_t i = 0; i < sprites_.size(); ++i) { - const auto& sprite = sprites_[i]; + for (const auto& sprite : sprites_) { const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool B = sprite->getRect().y < Options::game.height; if (A && B) { - const Uint8 COLOR = colors_[i % colors_.size()]; - sprite->renderWithVerticalFade(FADE_H, Options::game.height, 1, COLOR); + sprite->render(); } } } // Dibuja los sprites con el texto void Ending2::renderSpriteTexts() { - for (size_t i = 0; i < sprite_texts_.size(); ++i) { - const auto& sprite = sprite_texts_[i]; + for (const auto& sprite : sprite_texts_) { const bool A = sprite->getRect().y + sprite->getRect().h > 0; const bool B = sprite->getRect().y < Options::game.height; if (A && B) { - const Uint8 COLOR = colors_[i % colors_.size()]; - sprite->renderWithVerticalFade(FADE_H, Options::game.height, 1, COLOR); + sprite->render(); } } } @@ -385,7 +441,9 @@ void Ending2::createSpriteTexts() { // Crea el sprite SDL_FRect pos = {X, Y, W, H}; - sprite_texts_.emplace_back(std::make_shared(surface, pos)); + sprite_texts_.emplace_back(std::make_shared(surface, pos)); + sprite_texts_.back()->setColorReplace(1, static_cast(PaletteColor::WHITE)); + sprite_texts_.back()->setProgress(1.0F); // comença invisible sprite_texts_.back()->setVelY(SPRITE_DESP_SPEED); Screen::get()->setRendererSurface(previuos_renderer); } @@ -416,7 +474,8 @@ void Ending2::createTexts() { // Crea el sprite SDL_FRect pos = {X + DX, Y, W, H}; - texts_.emplace_back(std::make_shared(surface, pos)); + texts_.emplace_back(std::make_shared(surface, pos)); + texts_.back()->setProgress(1.0F); // comença invisible texts_.back()->setVelY(SPRITE_DESP_SPEED); Screen::get()->setRendererSurface(previuos_renderer); } @@ -445,7 +504,8 @@ void Ending2::createTexts() { // Crea el sprite SDL_FRect pos = {X + DX, Y, W, H}; - texts_.emplace_back(std::make_shared(surface, pos)); + texts_.emplace_back(std::make_shared(surface, pos)); + texts_.back()->setProgress(1.0F); // comença invisible texts_.back()->setVelY(SPRITE_DESP_SPEED); Screen::get()->setRendererSurface(previuos_renderer); } diff --git a/source/game/scenes/ending2.hpp b/source/game/scenes/ending2.hpp index 77c97a0..b924bb6 100644 --- a/source/game/scenes/ending2.hpp +++ b/source/game/scenes/ending2.hpp @@ -7,8 +7,10 @@ #include // Para vector #include "utils/defines.hpp" // Para GameCanvas::WIDTH, GameCanvas::FIRST_QUAR... -class SurfaceAnimatedSprite; // lines 9-9 -class SurfaceMovingSprite; // lines 10-10 + +#include "core/rendering/surface_dissolve_sprite.hpp" // Para SurfaceDissolveSprite + +class SurfaceMovingSprite; class DeltaTimer; class Ending2 { @@ -43,6 +45,8 @@ class Ending2 { static constexpr int INITIAL_Y_OFFSET = 40; // Offset inicial en Y para posicionar sprites static constexpr int SCREEN_MESH_HEIGHT = 8; // Altura de la malla superior/inferior de la pantalla static constexpr int FADE_H = 24; // Alçada de la zona de dissolució als cantons (files) + static constexpr float TRANSITION_DURATION_MS = 500.0F; // ms per canviar d'estat (generar o dissoldre) + static constexpr int ENTRY_EXIT_PADDING = 2; // px de padding als bordes per activar dissolució/generació static constexpr int TEXT_SPACING_MULTIPLIER = 2; // Multiplicador para espaciado entre líneas de texto // Constantes de tiempo (basadas en tiempo real, no en frames) @@ -74,9 +78,9 @@ class Ending2 { // --- Variables miembro --- // Objetos y punteros a recursos - std::vector> sprites_; // Vector con todos los sprites a dibujar - std::vector> sprite_texts_; // Vector con los sprites de texto de los sprites - std::vector> texts_; // Vector con los sprites de texto + std::vector> sprites_; // Vector con todos los sprites a dibujar + std::vector> sprite_texts_; // Vector con los sprites de texto de los sprites + std::vector> texts_; // Vector con los sprites de texto std::unique_ptr delta_timer_; // Timer para time-based update // Variables de estado