Files
orni-attack/source/core/graphics/border.cpp
T

108 lines
4.7 KiB
C++

// border.cpp - Implementació del border del playfield
// © 2026 JailDesigner
#include "core/graphics/border.hpp"
#include <algorithm>
#include <array>
#include "core/defaults.hpp"
#include "core/rendering/line_renderer.hpp"
namespace Graphics {
Border::Border(Rendering::Renderer* renderer)
: renderer_(renderer) {}
void Border::update(float delta_time) {
for (auto& side : sides_) {
// Desplaçament decau cap a 0 amb ritme constant (lineal).
const float DEC = Defaults::Border::DISPLACEMENT_RECOVERY_PER_S * delta_time;
side.displacement_px = std::max(0.0F, side.displacement_px - DEC);
}
}
void Border::bumpAt(Vec2 contact_point, float strength) {
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
const std::array<float, SIDE_COUNT> DISTANCES = {
/* TOP */ std::abs(contact_point.y - zone.y),
/* RIGHT */ std::abs((zone.x + zone.w) - contact_point.x),
/* BOTTOM */ std::abs((zone.y + zone.h) - contact_point.y),
/* LEFT */ std::abs(contact_point.x - zone.x)};
int closest_idx = 0;
float closest_dist = DISTANCES[0];
for (int i = 1; i < SIDE_COUNT; i++) {
if (DISTANCES[i] < closest_dist) {
closest_dist = DISTANCES[i];
closest_idx = i;
}
}
applyBump(closest_idx, strength);
}
void Border::applyBump(int side_idx, float strength) {
const float S = std::clamp(strength, 0.0F, 1.0F);
SideState& side = sides_[static_cast<std::size_t>(side_idx)];
side.displacement_px = std::min(
Defaults::Border::MAX_DISPLACEMENT_PX,
side.displacement_px + (S * Defaults::Border::MAX_DISPLACEMENT_PX));
}
namespace {
// Lerp del color base verd fòsfor cap a un color "flash" en funció de
// f ∈ [0, 1]. Retorna sempre amb alpha>0 perquè el line_renderer l'usi
// directament (sense caure al fallback DEFAULT_LINE_COLOR).
auto lerpColor(SDL_Color flash, float f) -> SDL_Color {
const float CLAMPED = std::clamp(f, 0.0F, 1.0F);
constexpr SDL_Color BASE = Rendering::DEFAULT_LINE_COLOR;
const auto LERP_U8 = [&](unsigned char a, unsigned char b) {
const float OUT = (static_cast<float>(a) * (1.0F - CLAMPED)) + (static_cast<float>(b) * CLAMPED);
return static_cast<unsigned char>(OUT);
};
return SDL_Color{
.r = LERP_U8(BASE.r, flash.r),
.g = LERP_U8(BASE.g, flash.g),
.b = LERP_U8(BASE.b, flash.b),
.a = 255};
}
} // namespace
void Border::draw() const {
const SDL_FRect& zone = Defaults::Zones::PLAYAREA;
const int X1 = static_cast<int>(zone.x);
const int Y1 = static_cast<int>(zone.y);
const int X2 = static_cast<int>(zone.x + zone.w);
const int Y2 = static_cast<int>(zone.y + zone.h);
const int OFF_TOP = static_cast<int>(sides_[SIDE_TOP].displacement_px);
const int OFF_RIGHT = static_cast<int>(sides_[SIDE_RIGHT].displacement_px);
const int OFF_BOTTOM = static_cast<int>(sides_[SIDE_BOTTOM].displacement_px);
const int OFF_LEFT = static_cast<int>(sides_[SIDE_LEFT].displacement_px);
// Color per costat: lerp(oscil·lador → flash) en funció del desplaçament.
const SDL_Color FLASH = {
.r = Defaults::Border::FLASH_COLOR_R,
.g = Defaults::Border::FLASH_COLOR_G,
.b = Defaults::Border::FLASH_COLOR_B,
.a = 255};
const float MAX_D = Defaults::Border::MAX_DISPLACEMENT_PX;
const bool DO_FLASH = Defaults::Border::FLASH_ENABLED;
const SDL_Color C_TOP = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_TOP].displacement_px / MAX_D) : SDL_Color{};
const SDL_Color C_RIGHT = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_RIGHT].displacement_px / MAX_D) : SDL_Color{};
const SDL_Color C_BOTTOM = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_BOTTOM].displacement_px / MAX_D) : SDL_Color{};
const SDL_Color C_LEFT = DO_FLASH ? lerpColor(FLASH, sides_[SIDE_LEFT].displacement_px / MAX_D) : SDL_Color{};
// Una sola línia per costat (brillo 1.0). Si DO_FLASH = false → alpha = 0 → usa
// el color global de l'oscil·lador.
Rendering::linea(renderer_, X1, Y1 - OFF_TOP, X2, Y1 - OFF_TOP, 1.0F, 0.0F, C_TOP);
Rendering::linea(renderer_, X2 + OFF_RIGHT, Y1, X2 + OFF_RIGHT, Y2, 1.0F, 0.0F, C_RIGHT);
Rendering::linea(renderer_, X1, Y2 + OFF_BOTTOM, X2, Y2 + OFF_BOTTOM, 1.0F, 0.0F, C_BOTTOM);
Rendering::linea(renderer_, X1 - OFF_LEFT, Y1, X1 - OFF_LEFT, Y2, 1.0F, 0.0F, C_LEFT);
}
} // namespace Graphics