Compare commits
3 Commits
c80dffe957
...
4e2393f4d5
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e2393f4d5 | |||
| d9ada12ee0 | |||
| a7d04d2bbc |
@@ -51,6 +51,7 @@ set(APP_SOURCES
|
|||||||
source/core/rendering/screen.cpp
|
source/core/rendering/screen.cpp
|
||||||
source/core/rendering/surface.cpp
|
source/core/rendering/surface.cpp
|
||||||
source/core/rendering/surface_animated_sprite.cpp
|
source/core/rendering/surface_animated_sprite.cpp
|
||||||
|
source/core/rendering/surface_dissolve_sprite.cpp
|
||||||
source/core/rendering/surface_moving_sprite.cpp
|
source/core/rendering/surface_moving_sprite.cpp
|
||||||
source/core/rendering/surface_sprite.cpp
|
source/core/rendering/surface_sprite.cpp
|
||||||
source/core/rendering/text.cpp
|
source/core/rendering/text.cpp
|
||||||
|
|||||||
@@ -428,6 +428,102 @@ void Surface::renderWithColorReplace(int x, int y, Uint8 source_color, Uint8 tar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hash 2D estable per a dithering sense flickering
|
||||||
|
static auto pixelThreshold(int col, int row) -> float {
|
||||||
|
auto h = static_cast<uint32_t>(col) * 2246822519U ^ static_cast<uint32_t>(row) * 2654435761U;
|
||||||
|
h ^= (h >> 13);
|
||||||
|
h *= 1274126177U;
|
||||||
|
h ^= (h >> 16);
|
||||||
|
return static_cast<float>(h & 0xFFFFU) / 65536.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig)
|
||||||
|
void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect) {
|
||||||
|
const int SX = src_rect ? static_cast<int>(src_rect->x) : 0;
|
||||||
|
const int SY = src_rect ? static_cast<int>(src_rect->y) : 0;
|
||||||
|
const int SW = src_rect ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width);
|
||||||
|
const int SH = src_rect ? static_cast<int>(src_rect->h) : static_cast<int>(surface_data_->height);
|
||||||
|
|
||||||
|
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
|
||||||
|
|
||||||
|
for (int row = 0; row < SH; row++) {
|
||||||
|
const int SCREEN_Y = y + row;
|
||||||
|
if (SCREEN_Y < 0 || SCREEN_Y >= static_cast<int>(surface_data_dest->height)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float density = 0.0F;
|
||||||
|
if (SCREEN_Y < fade_h) {
|
||||||
|
density = static_cast<float>(fade_h - SCREEN_Y) / static_cast<float>(fade_h);
|
||||||
|
} else if (SCREEN_Y >= canvas_height - fade_h) {
|
||||||
|
density = static_cast<float>(SCREEN_Y - (canvas_height - fade_h)) / static_cast<float>(fade_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int col = 0; col < SW; col++) {
|
||||||
|
const int SCREEN_X = x + col;
|
||||||
|
if (SCREEN_X < 0 || SCREEN_X >= static_cast<int>(surface_data_dest->width)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Uint8 COLOR = surface_data_->data[(SY + row) * static_cast<int>(surface_data_->width) + (SX + col)];
|
||||||
|
if (static_cast<int>(COLOR) == transparent_color_) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pixelThreshold(col, row) < density) {
|
||||||
|
continue; // Pixel tapat per la zona de fade
|
||||||
|
}
|
||||||
|
|
||||||
|
surface_data_dest->data[SCREEN_X + (SCREEN_Y * static_cast<int>(surface_data_dest->width))] = sub_palette_[COLOR];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Idem però reemplaçant un color índex
|
||||||
|
void Surface::renderWithVerticalFade(int x, int y, int fade_h, int canvas_height,
|
||||||
|
Uint8 source_color, Uint8 target_color,
|
||||||
|
SDL_FRect* src_rect) {
|
||||||
|
const int SX = src_rect ? static_cast<int>(src_rect->x) : 0;
|
||||||
|
const int SY = src_rect ? static_cast<int>(src_rect->y) : 0;
|
||||||
|
const int SW = src_rect ? static_cast<int>(src_rect->w) : static_cast<int>(surface_data_->width);
|
||||||
|
const int SH = src_rect ? static_cast<int>(src_rect->h) : static_cast<int>(surface_data_->height);
|
||||||
|
|
||||||
|
auto surface_data_dest = Screen::get()->getRendererSurface()->getSurfaceData();
|
||||||
|
|
||||||
|
for (int row = 0; row < SH; row++) {
|
||||||
|
const int SCREEN_Y = y + row;
|
||||||
|
if (SCREEN_Y < 0 || SCREEN_Y >= static_cast<int>(surface_data_dest->height)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float density = 0.0F;
|
||||||
|
if (SCREEN_Y < fade_h) {
|
||||||
|
density = static_cast<float>(fade_h - SCREEN_Y) / static_cast<float>(fade_h);
|
||||||
|
} else if (SCREEN_Y >= canvas_height - fade_h) {
|
||||||
|
density = static_cast<float>(SCREEN_Y - (canvas_height - fade_h)) / static_cast<float>(fade_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int col = 0; col < SW; col++) {
|
||||||
|
const int SCREEN_X = x + col;
|
||||||
|
if (SCREEN_X < 0 || SCREEN_X >= static_cast<int>(surface_data_dest->width)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Uint8 COLOR = surface_data_->data[(SY + row) * static_cast<int>(surface_data_->width) + (SX + col)];
|
||||||
|
if (static_cast<int>(COLOR) == transparent_color_) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pixelThreshold(col, row) < density) {
|
||||||
|
continue; // Pixel tapat per la zona de fade
|
||||||
|
}
|
||||||
|
|
||||||
|
const Uint8 OUT_COLOR = (COLOR == source_color) ? target_color : sub_palette_[COLOR];
|
||||||
|
surface_data_dest->data[SCREEN_X + (SCREEN_Y * static_cast<int>(surface_data_dest->width))] = OUT_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Vuelca la superficie a una textura
|
// Vuelca la superficie a una textura
|
||||||
void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) {
|
void Surface::copyToTexture(SDL_Renderer* renderer, SDL_Texture* texture) {
|
||||||
if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) {
|
if ((renderer == nullptr) || (texture == nullptr) || !surface_data_) {
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ class Surface {
|
|||||||
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
|
// Copia una región de la SurfaceData de origen a la SurfaceData de destino reemplazando un color por otro
|
||||||
void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_FRect* src_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
|
void renderWithColorReplace(int x, int y, Uint8 source_color = 0, Uint8 target_color = 0, SDL_FRect* src_rect = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE);
|
||||||
|
|
||||||
|
// Render amb dissolució als cantons superior/inferior (hash 2D, sense parpelleig)
|
||||||
|
void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height, SDL_FRect* src_rect = nullptr);
|
||||||
|
|
||||||
|
// Idem però reemplaçant un color índex (per a sprites sobre fons del mateix color)
|
||||||
|
void renderWithVerticalFade(int x, int y, int fade_h, int canvas_height,
|
||||||
|
Uint8 source_color, Uint8 target_color,
|
||||||
|
SDL_FRect* src_rect = nullptr);
|
||||||
|
|
||||||
// Establece un color en la paleta
|
// Establece un color en la paleta
|
||||||
void setColor(int index, Uint32 color);
|
void setColor(int index, Uint32 color);
|
||||||
|
|
||||||
|
|||||||
@@ -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> 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<float>(surface_->getWidth()), static_cast<float>(surface_->getHeight())};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Obtiene el indice de la animación a partir del nombre
|
// Obtiene el indice de la animación a partir del nombre
|
||||||
auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int {
|
auto SurfaceAnimatedSprite::getIndex(const std::string& name) -> int {
|
||||||
auto index = -1;
|
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)
|
// Calcula el frame correspondiente a la animación (time-based)
|
||||||
void SurfaceAnimatedSprite::animate(float delta_time) {
|
void SurfaceAnimatedSprite::animate(float delta_time) {
|
||||||
|
if (animations_.empty()) return;
|
||||||
if (animations_[current_animation_].speed <= 0.0F) {
|
if (animations_[current_animation_].speed <= 0.0F) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ class SurfaceAnimatedSprite : public SurfaceMovingSprite {
|
|||||||
void setCurrentAnimationFrame(int num); // Establece el frame actual de la animación
|
void setCurrentAnimationFrame(int num); // Establece el frame actual de la animación
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Constructor per a ús de subclasses que gestionen la surface directament (sense YAML)
|
||||||
|
SurfaceAnimatedSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
|
||||||
|
|
||||||
// Métodos protegidos
|
// Métodos protegidos
|
||||||
void animate(float delta_time); // Calcula el frame correspondiente a la animación actual (time-based)
|
void animate(float delta_time); // Calcula el frame correspondiente a la animación actual (time-based)
|
||||||
|
|
||||||
|
|||||||
189
source/core/rendering/surface_dissolve_sprite.cpp
Normal file
189
source/core/rendering/surface_dissolve_sprite.cpp
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
#include "core/rendering/surface_dissolve_sprite.hpp"
|
||||||
|
|
||||||
|
#include <algorithm> // Para min
|
||||||
|
#include <cstdint> // Para uint32_t
|
||||||
|
|
||||||
|
#include "core/rendering/surface.hpp" // Para Surface
|
||||||
|
|
||||||
|
// Hash 2D estable per a dithering (rank aleatori per posició de píxel)
|
||||||
|
static auto pixelRank(int col, int row) -> float {
|
||||||
|
auto h = static_cast<uint32_t>(col) * 2246822519U ^ static_cast<uint32_t>(row) * 2654435761U;
|
||||||
|
h ^= (h >> 13);
|
||||||
|
h *= 1274126177U;
|
||||||
|
h ^= (h >> 16);
|
||||||
|
return static_cast<float>(h & 0xFFFFU) / 65536.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rang per a un píxel tenint en compte direcció (70% direccional + 30% aleatori)
|
||||||
|
auto SurfaceDissolveSprite::computePixelRank(int col, int row, int frame_h,
|
||||||
|
DissolveDirection dir) -> float {
|
||||||
|
const float RANDOM = pixelRank(col, row);
|
||||||
|
if (dir == DissolveDirection::NONE || frame_h <= 0) {
|
||||||
|
return RANDOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
float y_factor = 0.0F;
|
||||||
|
if (dir == DissolveDirection::DOWN) {
|
||||||
|
y_factor = static_cast<float>(row) / static_cast<float>(frame_h);
|
||||||
|
} else {
|
||||||
|
y_factor = static_cast<float>(frame_h - 1 - row) / static_cast<float>(frame_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
return y_factor * 0.7F + RANDOM * 0.3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor per a surface directa (sense AnimationResource)
|
||||||
|
SurfaceDissolveSprite::SurfaceDissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos)
|
||||||
|
: SurfaceAnimatedSprite(std::move(surface), pos) {
|
||||||
|
if (surface_) {
|
||||||
|
const int W = static_cast<int>(surface_->getWidth());
|
||||||
|
const int H = static_cast<int>(surface_->getHeight());
|
||||||
|
surface_display_ = std::make_shared<Surface>(W, H);
|
||||||
|
surface_display_->setTransparentColor(surface_->getTransparentColor());
|
||||||
|
surface_display_->clear(surface_->getTransparentColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
SurfaceDissolveSprite::SurfaceDissolveSprite(const AnimationResource& data)
|
||||||
|
: SurfaceAnimatedSprite(data) {
|
||||||
|
if (surface_) {
|
||||||
|
const int W = static_cast<int>(surface_->getWidth());
|
||||||
|
const int H = static_cast<int>(surface_->getHeight());
|
||||||
|
surface_display_ = std::make_shared<Surface>(W, H);
|
||||||
|
surface_display_->setTransparentColor(surface_->getTransparentColor());
|
||||||
|
// Inicialitza tots els píxels com a transparents
|
||||||
|
surface_display_->clear(surface_->getTransparentColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstrueix la surface_display_ filtrant píxels per progress_
|
||||||
|
void SurfaceDissolveSprite::rebuildDisplaySurface() {
|
||||||
|
if (!surface_ || !surface_display_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SDL_FRect CLIP = clip_;
|
||||||
|
const int SX = static_cast<int>(CLIP.x);
|
||||||
|
const int SY = static_cast<int>(CLIP.y);
|
||||||
|
const int SW = static_cast<int>(CLIP.w);
|
||||||
|
const int SH = static_cast<int>(CLIP.h);
|
||||||
|
|
||||||
|
if (SW <= 0 || SH <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto src_data = surface_->getSurfaceData();
|
||||||
|
auto dst_data = surface_display_->getSurfaceData();
|
||||||
|
|
||||||
|
const int SRC_W = static_cast<int>(src_data->width);
|
||||||
|
const int DST_W = static_cast<int>(dst_data->width);
|
||||||
|
const Uint8 TRANSPARENT = surface_->getTransparentColor();
|
||||||
|
|
||||||
|
// Esborra frame anterior si ha canviat
|
||||||
|
if (prev_clip_.w > 0 && prev_clip_.h > 0 &&
|
||||||
|
(prev_clip_.x != CLIP.x || prev_clip_.y != CLIP.y ||
|
||||||
|
prev_clip_.w != CLIP.w || prev_clip_.h != CLIP.h)) {
|
||||||
|
surface_display_->fillRect(&prev_clip_, TRANSPARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Esborra la zona del frame actual (reconstrucció neta)
|
||||||
|
surface_display_->fillRect(&CLIP, TRANSPARENT);
|
||||||
|
|
||||||
|
// Copia píxels filtrats per progress_
|
||||||
|
for (int row = 0; row < SH; ++row) {
|
||||||
|
for (int col = 0; col < SW; ++col) {
|
||||||
|
const Uint8 COLOR = src_data->data[(SY + row) * SRC_W + (SX + col)];
|
||||||
|
if (COLOR == TRANSPARENT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const float RANK = computePixelRank(col, row, SH, direction_);
|
||||||
|
if (RANK >= progress_) {
|
||||||
|
const Uint8 OUT = (COLOR == source_color_) ? target_color_ : COLOR;
|
||||||
|
dst_data->data[(SY + row) * DST_W + (SX + col)] = OUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_clip_ = CLIP;
|
||||||
|
needs_rebuild_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualitza animació, moviment i transició temporal
|
||||||
|
void SurfaceDissolveSprite::update(float delta_time) {
|
||||||
|
const SDL_FRect OLD_CLIP = clip_;
|
||||||
|
SurfaceAnimatedSprite::update(delta_time);
|
||||||
|
|
||||||
|
// Detecta canvi de frame d'animació
|
||||||
|
if (clip_.x != OLD_CLIP.x || clip_.y != OLD_CLIP.y ||
|
||||||
|
clip_.w != OLD_CLIP.w || clip_.h != OLD_CLIP.h) {
|
||||||
|
needs_rebuild_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualitza transició temporal si activa
|
||||||
|
if (transition_mode_ != TransitionMode::NONE) {
|
||||||
|
transition_elapsed_ += delta_time * 1000.0F;
|
||||||
|
const float T = std::min(transition_elapsed_ / transition_duration_, 1.0F);
|
||||||
|
progress_ = (transition_mode_ == TransitionMode::DISSOLVING) ? T : (1.0F - T);
|
||||||
|
needs_rebuild_ = true;
|
||||||
|
if (T >= 1.0F) {
|
||||||
|
transition_mode_ = TransitionMode::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needs_rebuild_) {
|
||||||
|
rebuildDisplaySurface();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renderitza: usa surface_display_ (amb color replace) si disponible
|
||||||
|
void SurfaceDissolveSprite::render() {
|
||||||
|
if (!surface_display_) {
|
||||||
|
SurfaceAnimatedSprite::render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
surface_display_->render(static_cast<int>(pos_.x), static_cast<int>(pos_.y), &clip_, flip_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Estableix el progrés manualment
|
||||||
|
void SurfaceDissolveSprite::setProgress(float progress) {
|
||||||
|
progress_ = std::min(std::max(progress, 0.0F), 1.0F);
|
||||||
|
needs_rebuild_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicia dissolució temporal (visible → invisible)
|
||||||
|
void SurfaceDissolveSprite::startDissolve(float duration_ms, DissolveDirection dir) {
|
||||||
|
direction_ = dir;
|
||||||
|
transition_mode_ = TransitionMode::DISSOLVING;
|
||||||
|
transition_duration_ = duration_ms;
|
||||||
|
transition_elapsed_ = 0.0F;
|
||||||
|
progress_ = 0.0F;
|
||||||
|
needs_rebuild_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inicia generació temporal (invisible → visible)
|
||||||
|
void SurfaceDissolveSprite::startGenerate(float duration_ms, DissolveDirection dir) {
|
||||||
|
direction_ = dir;
|
||||||
|
transition_mode_ = TransitionMode::GENERATING;
|
||||||
|
transition_duration_ = duration_ms;
|
||||||
|
transition_elapsed_ = 0.0F;
|
||||||
|
progress_ = 1.0F;
|
||||||
|
needs_rebuild_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atura la transició temporal
|
||||||
|
void SurfaceDissolveSprite::stopTransition() {
|
||||||
|
transition_mode_ = TransitionMode::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retorna si la transició ha acabat
|
||||||
|
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;
|
||||||
|
}
|
||||||
59
source/core/rendering/surface_dissolve_sprite.hpp
Normal file
59
source/core/rendering/surface_dissolve_sprite.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <memory> // Para shared_ptr
|
||||||
|
|
||||||
|
#include "core/rendering/surface_animated_sprite.hpp" // Para SurfaceAnimatedSprite
|
||||||
|
|
||||||
|
class Surface;
|
||||||
|
|
||||||
|
// Direcció de la dissolució
|
||||||
|
enum class DissolveDirection { NONE, DOWN, UP };
|
||||||
|
|
||||||
|
// Sprite que pot dissoldre's o generar-se de forma aleatòria en X mil·lisegons.
|
||||||
|
// progress_ va de 0.0 (totalment visible) a 1.0 (totalment invisible).
|
||||||
|
class SurfaceDissolveSprite : public SurfaceAnimatedSprite {
|
||||||
|
public:
|
||||||
|
explicit SurfaceDissolveSprite(const AnimationResource& data);
|
||||||
|
SurfaceDissolveSprite(std::shared_ptr<Surface> surface, SDL_FRect pos);
|
||||||
|
~SurfaceDissolveSprite() override = default;
|
||||||
|
|
||||||
|
void update(float delta_time) override;
|
||||||
|
void render() override;
|
||||||
|
|
||||||
|
// Progrés manual [0.0 = totalment visible, 1.0 = totalment invisible]
|
||||||
|
void setProgress(float progress);
|
||||||
|
[[nodiscard]] auto getProgress() const -> float { return progress_; }
|
||||||
|
|
||||||
|
// Inicia una dissolució temporal (visible → invisible en duration_ms)
|
||||||
|
void startDissolve(float duration_ms, DissolveDirection dir = DissolveDirection::NONE);
|
||||||
|
|
||||||
|
// Inicia una generació temporal (invisible → visible en duration_ms)
|
||||||
|
void startGenerate(float duration_ms, DissolveDirection dir = DissolveDirection::NONE);
|
||||||
|
|
||||||
|
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 };
|
||||||
|
|
||||||
|
std::shared_ptr<Surface> surface_display_; // Superfície amb els píxels filtrats
|
||||||
|
|
||||||
|
float progress_{0.0F}; // [0=visible, 1=invisible]
|
||||||
|
DissolveDirection direction_{DissolveDirection::NONE};
|
||||||
|
TransitionMode transition_mode_{TransitionMode::NONE};
|
||||||
|
float transition_duration_{0.0F};
|
||||||
|
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,
|
||||||
|
DissolveDirection dir) -> float;
|
||||||
|
};
|
||||||
@@ -31,6 +31,19 @@ void SurfaceSprite::render(Uint8 source_color, Uint8 target_color) {
|
|||||||
surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_);
|
surface_->renderWithColorReplace(pos_.x, pos_.y, source_color, target_color, &clip_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height) {
|
||||||
|
surface_->renderWithVerticalFade(
|
||||||
|
static_cast<int>(pos_.x), static_cast<int>(pos_.y),
|
||||||
|
fade_h, canvas_height, &clip_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SurfaceSprite::renderWithVerticalFade(int fade_h, int canvas_height,
|
||||||
|
Uint8 source_color, Uint8 target_color) {
|
||||||
|
surface_->renderWithVerticalFade(
|
||||||
|
static_cast<int>(pos_.x), static_cast<int>(pos_.y),
|
||||||
|
fade_h, canvas_height, source_color, target_color, &clip_);
|
||||||
|
}
|
||||||
|
|
||||||
// Establece la posición del objeto
|
// Establece la posición del objeto
|
||||||
void SurfaceSprite::setPosition(float x, float y) {
|
void SurfaceSprite::setPosition(float x, float y) {
|
||||||
pos_.x = x;
|
pos_.x = x;
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class SurfaceSprite {
|
|||||||
virtual void update(float delta_time); // Actualiza el estado del sprite (time-based)
|
virtual void update(float delta_time); // Actualiza el estado del sprite (time-based)
|
||||||
virtual void render(); // Muestra el sprite por pantalla
|
virtual void render(); // Muestra el sprite por pantalla
|
||||||
virtual void render(Uint8 source_color, Uint8 target_color); // Renderiza con reemplazo de color
|
virtual void render(Uint8 source_color, Uint8 target_color); // Renderiza con reemplazo de color
|
||||||
|
virtual void renderWithVerticalFade(int fade_h, int canvas_height); // Renderiza amb dissolució vertical (hash 2D, sense parpelleig)
|
||||||
|
virtual void renderWithVerticalFade(int fade_h, int canvas_height, Uint8 source_color, Uint8 target_color); // Idem amb reemplaç de color
|
||||||
|
|
||||||
// Gestión de estado
|
// Gestión de estado
|
||||||
virtual void clear(); // Reinicia las variables a cero
|
virtual void clear(); // Reinicia las variables a cero
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ enum class Options {
|
|||||||
|
|
||||||
// --- Variables de estado globales ---
|
// --- Variables de estado globales ---
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
inline Scene current = Scene::CREDITS; // Escena actual
|
inline Scene current = Scene::ENDING2; // Escena actual
|
||||||
inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual
|
inline Options options = Options::LOGO_TO_LOADING_SCREEN; // Opciones de la escena actual
|
||||||
#else
|
#else
|
||||||
inline Scene current = Scene::LOGO; // Escena actual
|
inline Scene current = Scene::LOGO; // Escena actual
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "core/input/input.hpp" // Para Input
|
#include "core/input/input.hpp" // Para Input
|
||||||
#include "core/rendering/screen.hpp" // Para Screen
|
#include "core/rendering/screen.hpp" // Para Screen
|
||||||
#include "core/rendering/surface.hpp" // Para Surface
|
#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/surface_moving_sprite.hpp" // Para SMovingSprite
|
||||||
#include "core/rendering/text.hpp" // Para Text
|
#include "core/rendering/text.hpp" // Para Text
|
||||||
#include "core/resources/resource_cache.hpp" // Para Resource
|
#include "core/resources/resource_cache.hpp" // Para Resource
|
||||||
@@ -90,27 +90,6 @@ void Ending2::render() {
|
|||||||
// Dibuja los sprites con el texto del final
|
// Dibuja los sprites con el texto del final
|
||||||
renderTexts();
|
renderTexts();
|
||||||
|
|
||||||
// Dibuja una trama arriba y abajo
|
|
||||||
auto color = static_cast<Uint8>(PaletteColor::BLACK);
|
|
||||||
auto surface = Screen::get()->getRendererSurface();
|
|
||||||
for (int i = 0; i < 256; i += 2) {
|
|
||||||
surface->putPixel(i + 0, 0, color);
|
|
||||||
surface->putPixel(i + 1, 1, color);
|
|
||||||
surface->putPixel(i + 0, 2, color);
|
|
||||||
surface->putPixel(i + 1, 3, color);
|
|
||||||
|
|
||||||
surface->putPixel(i, 4, color);
|
|
||||||
surface->putPixel(i, 6, color);
|
|
||||||
|
|
||||||
surface->putPixel(i + 0, 191, color);
|
|
||||||
surface->putPixel(i + 1, 190, color);
|
|
||||||
surface->putPixel(i + 0, 189, color);
|
|
||||||
surface->putPixel(i + 1, 188, color);
|
|
||||||
|
|
||||||
surface->putPixel(i, 187, color);
|
|
||||||
surface->putPixel(i, 185, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vuelca el contenido del renderizador en pantalla
|
// Vuelca el contenido del renderizador en pantalla
|
||||||
Screen::get()->render();
|
Screen::get()->render();
|
||||||
}
|
}
|
||||||
@@ -167,7 +146,7 @@ void Ending2::updateState(float delta_time) {
|
|||||||
case EndingState::FADING:
|
case EndingState::FADING:
|
||||||
if (state_time_ >= STATE_FADE_DURATION) {
|
if (state_time_ >= STATE_FADE_DURATION) {
|
||||||
SceneManager::current = SceneManager::Scene::LOGO;
|
SceneManager::current = SceneManager::Scene::LOGO;
|
||||||
SceneManager::options = SceneManager::Options::LOGO_TO_LOADING_SCREEN;
|
SceneManager::options = SceneManager::Options::LOGO_TO_TITLE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -288,16 +267,40 @@ void Ending2::loadSprites() {
|
|||||||
// Carga los sprites
|
// Carga los sprites
|
||||||
for (const auto& file : sprite_list_) {
|
for (const auto& file : sprite_list_) {
|
||||||
const auto& animation_data = Resource::Cache::get()->getAnimationData(file + ".yaml");
|
const auto& animation_data = Resource::Cache::get()->getAnimationData(file + ".yaml");
|
||||||
sprites_.emplace_back(std::make_shared<SurfaceAnimatedSprite>(animation_data));
|
sprites_.emplace_back(std::make_shared<SurfaceDissolveSprite>(animation_data));
|
||||||
|
sprites_.back()->setColorReplace(1, static_cast<Uint8>(PaletteColor::RED));
|
||||||
|
sprites_.back()->setProgress(1.0F); // comença invisible
|
||||||
sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_);
|
sprite_max_width_ = std::max(sprites_.back()->getWidth(), sprite_max_width_);
|
||||||
sprite_max_height_ = std::max(sprites_.back()->getHeight(), sprite_max_height_);
|
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<Uint8>(PaletteColor::WHITE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualiza los sprites
|
// Actualiza los sprites
|
||||||
void Ending2::updateSprites(float delta) {
|
void Ending2::updateSprites(float delta) {
|
||||||
for (const auto& sprite : sprites_) {
|
for (const auto& sprite : sprites_) {
|
||||||
sprite->update(delta);
|
sprite->update(delta);
|
||||||
|
|
||||||
|
const float Y = sprite->getPosY();
|
||||||
|
const float H = sprite->getHeight();
|
||||||
|
const float CANVAS_H = static_cast<float>(Options::game.height);
|
||||||
|
|
||||||
|
// Checkpoint inferior: sprite entra per baix → generar de dalt a baix
|
||||||
|
if (Y > static_cast<float>(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<float>(ENTRY_EXIT_PADDING)
|
||||||
|
&& sprite->getProgress() <= 0.0F
|
||||||
|
&& sprite->isTransitionDone()) {
|
||||||
|
sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,6 +308,23 @@ void Ending2::updateSprites(float delta) {
|
|||||||
void Ending2::updateTextSprites(float delta) {
|
void Ending2::updateTextSprites(float delta) {
|
||||||
for (const auto& sprite : sprite_texts_) {
|
for (const auto& sprite : sprite_texts_) {
|
||||||
sprite->update(delta);
|
sprite->update(delta);
|
||||||
|
|
||||||
|
const float Y = sprite->getPosY();
|
||||||
|
const float H = sprite->getHeight();
|
||||||
|
const float CANVAS_H = static_cast<float>(Options::game.height);
|
||||||
|
|
||||||
|
if (Y > static_cast<float>(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<float>(ENTRY_EXIT_PADDING)
|
||||||
|
&& sprite->getProgress() <= 0.0F
|
||||||
|
&& sprite->isTransitionDone()) {
|
||||||
|
sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,33 +332,46 @@ void Ending2::updateTextSprites(float delta) {
|
|||||||
void Ending2::updateTexts(float delta) {
|
void Ending2::updateTexts(float delta) {
|
||||||
for (const auto& sprite : texts_) {
|
for (const auto& sprite : texts_) {
|
||||||
sprite->update(delta);
|
sprite->update(delta);
|
||||||
|
|
||||||
|
const float Y = sprite->getPosY();
|
||||||
|
const float H = sprite->getHeight();
|
||||||
|
const float CANVAS_H = static_cast<float>(Options::game.height);
|
||||||
|
|
||||||
|
// Checkpoint inferior: text entra per baix → generar de dalt a baix
|
||||||
|
if (Y > static_cast<float>(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<float>(ENTRY_EXIT_PADDING)
|
||||||
|
&& sprite->getProgress() <= 0.0F
|
||||||
|
&& sprite->isTransitionDone()) {
|
||||||
|
sprite->startDissolve(TRANSITION_DURATION_MS, DissolveDirection::DOWN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibuja los sprites
|
// Dibuja los sprites
|
||||||
void Ending2::renderSprites() {
|
void Ending2::renderSprites() {
|
||||||
const auto COLOR_A = static_cast<Uint8>(PaletteColor::RED);
|
|
||||||
for (const auto& sprite : sprites_) {
|
for (const auto& sprite : sprites_) {
|
||||||
const bool A = sprite->getRect().y + sprite->getRect().h > 0;
|
const bool A = sprite->getRect().y + sprite->getRect().h > 0;
|
||||||
const bool B = sprite->getRect().y < Options::game.height;
|
const bool B = sprite->getRect().y < Options::game.height;
|
||||||
if (A && B) {
|
if (A && B) {
|
||||||
sprite->render(1, COLOR_A);
|
sprite->render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pinta el ultimo elemento de otro color
|
|
||||||
const auto COLOR_B = static_cast<Uint8>(PaletteColor::WHITE);
|
|
||||||
sprites_.back()->render(1, COLOR_B);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibuja los sprites con el texto
|
// Dibuja los sprites con el texto
|
||||||
void Ending2::renderSpriteTexts() {
|
void Ending2::renderSpriteTexts() {
|
||||||
const auto COLOR = static_cast<Uint8>(PaletteColor::WHITE);
|
|
||||||
for (const auto& sprite : sprite_texts_) {
|
for (const auto& sprite : sprite_texts_) {
|
||||||
const bool A = sprite->getRect().y + sprite->getRect().h > 0;
|
const bool A = sprite->getRect().y + sprite->getRect().h > 0;
|
||||||
const bool B = sprite->getRect().y < Options::game.height;
|
const bool B = sprite->getRect().y < Options::game.height;
|
||||||
if (A && B) {
|
if (A && B) {
|
||||||
sprite->render(1, COLOR);
|
sprite->render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,7 +441,9 @@ void Ending2::createSpriteTexts() {
|
|||||||
|
|
||||||
// Crea el sprite
|
// Crea el sprite
|
||||||
SDL_FRect pos = {X, Y, W, H};
|
SDL_FRect pos = {X, Y, W, H};
|
||||||
sprite_texts_.emplace_back(std::make_shared<SurfaceMovingSprite>(surface, pos));
|
sprite_texts_.emplace_back(std::make_shared<SurfaceDissolveSprite>(surface, pos));
|
||||||
|
sprite_texts_.back()->setColorReplace(1, static_cast<Uint8>(PaletteColor::WHITE));
|
||||||
|
sprite_texts_.back()->setProgress(1.0F); // comença invisible
|
||||||
sprite_texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
sprite_texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
||||||
Screen::get()->setRendererSurface(previuos_renderer);
|
Screen::get()->setRendererSurface(previuos_renderer);
|
||||||
}
|
}
|
||||||
@@ -439,7 +474,8 @@ void Ending2::createTexts() {
|
|||||||
|
|
||||||
// Crea el sprite
|
// Crea el sprite
|
||||||
SDL_FRect pos = {X + DX, Y, W, H};
|
SDL_FRect pos = {X + DX, Y, W, H};
|
||||||
texts_.emplace_back(std::make_shared<SurfaceMovingSprite>(surface, pos));
|
texts_.emplace_back(std::make_shared<SurfaceDissolveSprite>(surface, pos));
|
||||||
|
texts_.back()->setProgress(1.0F); // comença invisible
|
||||||
texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
||||||
Screen::get()->setRendererSurface(previuos_renderer);
|
Screen::get()->setRendererSurface(previuos_renderer);
|
||||||
}
|
}
|
||||||
@@ -468,7 +504,8 @@ void Ending2::createTexts() {
|
|||||||
|
|
||||||
// Crea el sprite
|
// Crea el sprite
|
||||||
SDL_FRect pos = {X + DX, Y, W, H};
|
SDL_FRect pos = {X + DX, Y, W, H};
|
||||||
texts_.emplace_back(std::make_shared<SurfaceMovingSprite>(surface, pos));
|
texts_.emplace_back(std::make_shared<SurfaceDissolveSprite>(surface, pos));
|
||||||
|
texts_.back()->setProgress(1.0F); // comença invisible
|
||||||
texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
texts_.back()->setVelY(SPRITE_DESP_SPEED);
|
||||||
Screen::get()->setRendererSurface(previuos_renderer);
|
Screen::get()->setRendererSurface(previuos_renderer);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
#include <vector> // Para vector
|
#include <vector> // Para vector
|
||||||
|
|
||||||
#include "utils/defines.hpp" // Para GameCanvas::WIDTH, GameCanvas::FIRST_QUAR...
|
#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 DeltaTimer;
|
||||||
|
|
||||||
class Ending2 {
|
class Ending2 {
|
||||||
@@ -42,6 +44,9 @@ class Ending2 {
|
|||||||
static constexpr int DIST_SPRITE_SPRITE = 0; // Distancia entre dos sprites de la misma columna
|
static constexpr int DIST_SPRITE_SPRITE = 0; // Distancia entre dos sprites de la misma columna
|
||||||
static constexpr int INITIAL_Y_OFFSET = 40; // Offset inicial en Y para posicionar sprites
|
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 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
|
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)
|
// Constantes de tiempo (basadas en tiempo real, no en frames)
|
||||||
@@ -73,9 +78,9 @@ class Ending2 {
|
|||||||
|
|
||||||
// --- Variables miembro ---
|
// --- Variables miembro ---
|
||||||
// Objetos y punteros a recursos
|
// Objetos y punteros a recursos
|
||||||
std::vector<std::shared_ptr<SurfaceAnimatedSprite>> sprites_; // Vector con todos los sprites a dibujar
|
std::vector<std::shared_ptr<SurfaceDissolveSprite>> sprites_; // Vector con todos los sprites a dibujar
|
||||||
std::vector<std::shared_ptr<SurfaceMovingSprite>> sprite_texts_; // Vector con los sprites de texto de los sprites
|
std::vector<std::shared_ptr<SurfaceDissolveSprite>> sprite_texts_; // Vector con los sprites de texto de los sprites
|
||||||
std::vector<std::shared_ptr<SurfaceMovingSprite>> texts_; // Vector con los sprites de texto
|
std::vector<std::shared_ptr<SurfaceDissolveSprite>> texts_; // Vector con los sprites de texto
|
||||||
std::unique_ptr<DeltaTimer> delta_timer_; // Timer para time-based update
|
std::unique_ptr<DeltaTimer> delta_timer_; // Timer para time-based update
|
||||||
|
|
||||||
// Variables de estado
|
// Variables de estado
|
||||||
|
|||||||
Reference in New Issue
Block a user