Files
coffee_crisis_arcade_edition/source/background.cpp
2024-10-10 20:59:39 +02:00

328 lines
11 KiB
C++

#include "background.h"
#include <SDL2/SDL_blendmode.h> // for SDL_BLENDMODE_BLEND
#include <SDL2/SDL_pixels.h> // for SDL_PIXELFORMAT_RGBA8888
#include <algorithm> // for max, min
#include <string> // for basic_string
#include "asset.h" // for Asset
#include "param.h" // for param
// Constructor
Background::Background(SDL_Renderer *renderer)
: renderer_(renderer)
{
// Carga las texturas
buildings_texture_ = std::make_shared<Texture>(renderer, Asset::get()->get("game_buildings.png"));
top_clouds_texture_ = std::make_shared<Texture>(renderer, Asset::get()->get("game_clouds1.png"));
bottom_clouds_texture_ = std::make_shared<Texture>(renderer, Asset::get()->get("game_clouds2.png"));
grass_texture_ = std::make_shared<Texture>(renderer, Asset::get()->get("game_grass.png"));
gradients_texture_ = std::make_shared<Texture>(renderer, Asset::get()->get("game_sky_colors.png"));
// Inicializa variables
gradient_number_ = 0;
alpha_ = 0;
clouds_speed_ = 0;
transition_ = 0;
counter_ = 0;
rect_ = {0, 0, gradients_texture_->getWidth() / 2, gradients_texture_->getHeight() / 2};
src_rect_ = {0, 0, 320, 240};
dst_rect_ = {0, 0, 320, 240};
base_ = rect_.h;
color_ = {param.background.attenuate_color.r, param.background.attenuate_color.g, param.background.attenuate_color.b};
alpha_color_text_ = alpha_color_text_temp_ = param.background.attenuate_alpha;
gradient_rect_[0] = {0, 0, rect_.w, rect_.h};
gradient_rect_[1] = {rect_.w, 0, rect_.w, rect_.h};
gradient_rect_[2] = {0, rect_.h, rect_.w, rect_.h};
gradient_rect_[3] = {rect_.w, rect_.h, rect_.w, rect_.h};
const int top_clouds_texture_height = top_clouds_texture_->getHeight() / 4;
const int bottom_clouds_texture_height = bottom_clouds_texture_->getHeight() / 4;
for (int i = 0; i < 4; ++i)
{
top_clouds_rect_[i] = {0, i * top_clouds_texture_height, top_clouds_texture_->getWidth(), top_clouds_texture_height};
bottom_clouds_rect_[i] = {0, i * bottom_clouds_texture_height, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_height};
}
// Crea los sprites
const int top_clouds_y = base_ - 165;
const int bottom_clouds_y = base_ - 101;
constexpr float top_clouds_speed = 0.1f;
constexpr float bottom_clouds_speed = 0.05f;
top_clouds_sprite_a_ = std::make_unique<MovingSprite>(0, top_clouds_y, rect_.w, top_clouds_texture_->getHeight(), -top_clouds_speed, 0.0f, 0.0f, 0.0f, top_clouds_texture_);
top_clouds_sprite_b_ = std::make_unique<MovingSprite>(rect_.w, top_clouds_y, rect_.w, top_clouds_texture_->getHeight(), -top_clouds_speed, 0.0f, 0.0f, 0.0f, top_clouds_texture_);
bottom_clouds_sprite_a_ = std::make_unique<MovingSprite>(0, bottom_clouds_y, rect_.w, bottom_clouds_texture_->getHeight(), -bottom_clouds_speed, 0.0f, 0.0f, 0.0f, bottom_clouds_texture_);
bottom_clouds_sprite_b_ = std::make_unique<MovingSprite>(rect_.w, bottom_clouds_y, rect_.w, bottom_clouds_texture_->getHeight(), -bottom_clouds_speed, 0.0f, 0.0f, 0.0f, bottom_clouds_texture_);
buildings_sprite_ = std::make_unique<Sprite>(0, 0, buildings_texture_->getWidth(), buildings_texture_->getHeight(), buildings_texture_);
gradient_sprite_ = std::make_unique<Sprite>(0, 0, rect_.w, rect_.h, gradients_texture_);
grass_sprite_ = std::make_unique<Sprite>(0, 0, grass_texture_->getWidth(), grass_texture_->getHeight() / 2, grass_texture_);
// Inicializa objetos
top_clouds_sprite_a_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight());
top_clouds_sprite_b_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight());
bottom_clouds_sprite_a_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight());
bottom_clouds_sprite_b_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight());
buildings_sprite_->setPosY(base_ - buildings_sprite_->getHeight());
grass_sprite_->setPosY(base_ - grass_sprite_->getHeight());
// Crea la textura para componer el fondo
canvas_ = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND);
// Crea la textura para atenuar el fondo
color_texture_ = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h);
SDL_SetTextureBlendMode(color_texture_, SDL_BLENDMODE_BLEND);
setColor(color_);
SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_);
}
// Destructor
Background::~Background()
{
SDL_DestroyTexture(canvas_);
SDL_DestroyTexture(color_texture_);
}
// Actualiza la lógica del objeto
void Background::update()
{
// Actualiza el valor de alpha_
updateAlphaColorText();
// Actualiza las nubes
updateClouds();
// Calcula el frame de la hierba
grass_sprite_->setSpriteClip(0, (10 * (counter_ / 20 % 2)), 320, 10);
// Calcula el valor de alpha_
alpha_ = std::max((255 - (int)(255 * transition_)), 0);
// Incrementa el contador
counter_++;
fillCanvas();
}
// Dibuja el gradiente de fondo
void Background::renderGradient()
{
// Dibuja el gradiente 2
gradients_texture_->setAlpha(255);
gradient_sprite_->setSpriteClip(gradient_rect_[(gradient_number_ + 1) % 4]);
gradient_sprite_->render();
// Dibuja el gradiente 1 con una opacidad cada vez menor
gradients_texture_->setAlpha(alpha_);
gradient_sprite_->setSpriteClip(gradient_rect_[gradient_number_]);
gradient_sprite_->render();
}
// Dibuja las nubes de arriba
void Background::renderTopClouds()
{
// Dibuja el primer conjunto de nubes
top_clouds_texture_->setAlpha(255);
top_clouds_sprite_a_->setSpriteClip(top_clouds_rect_[(gradient_number_ + 1) % 4]);
top_clouds_sprite_a_->render();
top_clouds_sprite_b_->setSpriteClip(top_clouds_rect_[(gradient_number_ + 1) % 4]);
top_clouds_sprite_b_->render();
// Dibuja el segundo conjunto de nubes
top_clouds_texture_->setAlpha(alpha_);
top_clouds_sprite_a_->setSpriteClip(top_clouds_rect_[gradient_number_]);
top_clouds_sprite_a_->render();
top_clouds_sprite_b_->setSpriteClip(top_clouds_rect_[gradient_number_]);
top_clouds_sprite_b_->render();
}
// Dibuja las nubes de abajo
void Background::renderBottomClouds()
{
// Dibuja el primer conjunto de nubes
bottom_clouds_texture_->setAlpha(255);
bottom_clouds_sprite_a_->setSpriteClip(bottom_clouds_rect_[(gradient_number_ + 1) % 4]);
bottom_clouds_sprite_a_->render();
bottom_clouds_sprite_b_->setSpriteClip(bottom_clouds_rect_[(gradient_number_ + 1) % 4]);
bottom_clouds_sprite_b_->render();
// Dibuja el segundo conjunto de nubes
bottom_clouds_texture_->setAlpha(alpha_);
bottom_clouds_sprite_a_->setSpriteClip(bottom_clouds_rect_[gradient_number_]);
bottom_clouds_sprite_a_->render();
bottom_clouds_sprite_b_->setSpriteClip(bottom_clouds_rect_[gradient_number_]);
bottom_clouds_sprite_b_->render();
}
// Compone todos los elementos del fondo en la textura
void Background::fillCanvas()
{
// Cambia el destino del renderizador
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, canvas_);
// Dibuja el gradiente de fondo
renderGradient();
// Dibuja las nubes de arriba
renderTopClouds();
// Dibuja las nubes de abajo
renderBottomClouds();
// Dibuja los edificios
buildings_sprite_->render();
// Dibuja la hierba
grass_sprite_->render();
// Deja el renderizador apuntando donde estaba
SDL_SetRenderTarget(renderer_, temp);
}
// Dibuja el objeto
void Background::render()
{
// Fondo
SDL_RenderCopy(renderer_, canvas_, &src_rect_, &dst_rect_);
// Atenuación
SDL_RenderCopy(renderer_, color_texture_, &src_rect_, &dst_rect_);
}
// Vuelve a cargar las texturas
void Background::reloadTextures()
{
buildings_texture_->reLoad();
top_clouds_texture_->reLoad();
bottom_clouds_texture_->reLoad();
grass_texture_->reLoad();
gradients_texture_->reLoad();
}
// Ajusta el valor de la variable
void Background::setCloudsSpeed(float value)
{
clouds_speed_ = value;
}
// Ajusta el valor de la variable
void Background::setGradientNumber(int value)
{
gradient_number_ = value % 4;
}
// Ajusta el valor de la variable
void Background::setTransition(float value)
{
value = std::min(value, 1.0f);
value = std::max(value, 0.0f);
transition_ = value;
}
// Establece la posición del objeto
void Background::setPos(SDL_Rect pos)
{
dst_rect_ = pos;
// Si cambian las medidas del destino, hay que cambiar las del origen para evitar deformar la imagen
src_rect_.x = 0;
src_rect_.y = rect_.h - pos.h;
src_rect_.w = pos.w;
src_rect_.h = pos.h;
}
// Ajusta el valor de la variable
void Background::setSrcRect(SDL_Rect value)
{
src_rect_ = value;
}
// Ajusta el valor de la variable
void Background::setDstRect(SDL_Rect value)
{
dst_rect_ = value;
}
// Establece el color_ de atenuación
void Background::setColor(Color color)
{
color_ = color;
// Colorea la textura
auto temp = SDL_GetRenderTarget(renderer_);
SDL_SetRenderTarget(renderer_, color_texture_);
SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, color_.b, 255);
SDL_RenderClear(renderer_);
SDL_SetRenderTarget(renderer_, temp);
}
// Establece la transparencia de la atenuación
void Background::setAlpha(int alpha)
{
// Evita que se asignen valores fuera de rango
alpha_ = std::min(alpha, 255);
alpha_ = std::max(alpha, 0);
// Guarda el valor actual
alpha_color_text_temp_ = alpha_color_text_;
// Establece el nuevo valor
alpha_color_text_ = alpha_;
}
// Actualiza el valor de alpha_
void Background::updateAlphaColorText()
{
if (alpha_color_text_ == alpha_color_text_temp_)
{
return;
}
else
{
alpha_color_text_ > alpha_color_text_temp_ ? ++alpha_color_text_temp_ : --alpha_color_text_temp_;
SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_temp_);
}
}
// Actualiza las nubes
void Background::updateClouds()
{
// Aplica la velocidad calculada a las nubes
top_clouds_sprite_a_->setVelX(clouds_speed_);
top_clouds_sprite_b_->setVelX(clouds_speed_);
bottom_clouds_sprite_a_->setVelX(clouds_speed_ / 2);
bottom_clouds_sprite_b_->setVelX(clouds_speed_ / 2);
// Mueve las nubes
top_clouds_sprite_a_->move();
top_clouds_sprite_b_->move();
bottom_clouds_sprite_a_->move();
bottom_clouds_sprite_b_->move();
// Calcula el offset de las nubes
if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth())
{
top_clouds_sprite_a_->setPosX(top_clouds_sprite_a_->getWidth());
}
if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth())
{
top_clouds_sprite_b_->setPosX(top_clouds_sprite_b_->getWidth());
}
if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth())
{
bottom_clouds_sprite_a_->setPosX(bottom_clouds_sprite_a_->getWidth());
}
if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth())
{
bottom_clouds_sprite_b_->setPosX(bottom_clouds_sprite_b_->getWidth());
}
}