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

113 lines
4.5 KiB
C++

// playfield.cpp - Implementació del fons del playfield
// © 2026 JailDesigner
#include "core/graphics/playfield.hpp"
#include <algorithm>
#include "core/defaults.hpp"
#include "core/rendering/line_renderer.hpp"
namespace Graphics {
Playfield::Playfield(Rendering::Renderer* renderer)
: renderer_(renderer) {
buildLines();
total_slots_ = static_cast<int>(lines_.size());
// Distribuïm els spawns de manera que la última línia acabe just a TOTAL_ANIMATION_DURATION_S.
// last_line_start = (N-1) * spawn_interval
// last_line_end = last_line_start + LINE_GROWTH_DURATION_S = TOTAL_ANIMATION_DURATION_S
if (total_slots_ > 1) {
spawn_interval_s_ =
(Defaults::Playfield::TOTAL_ANIMATION_DURATION_S - Defaults::Playfield::LINE_GROWTH_DURATION_S) / static_cast<float>(total_slots_ - 1);
}
}
void Playfield::update(float delta_time) {
elapsed_s_ += delta_time;
}
void Playfield::buildLines() {
const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
const float CELL_W = zona.w / static_cast<float>(Defaults::Playfield::COLUMNS);
const float CELL_H = zona.h / static_cast<float>(Defaults::Playfield::ROWS);
const float SUB_W = CELL_W / static_cast<float>(Defaults::Playfield::SUBDIVISIONS);
const float SUB_H = CELL_H / static_cast<float>(Defaults::Playfield::SUBDIVISIONS);
const int SUB_VERTS = Defaults::Playfield::COLUMNS * Defaults::Playfield::SUBDIVISIONS;
const int SUB_HORIZ = Defaults::Playfield::ROWS * Defaults::Playfield::SUBDIVISIONS;
std::vector<Line> verticals;
std::vector<Line> horizontals;
// Verticals: posicions i ∈ [1, SUB_VERTS-1]. Si i % SUBDIVISIONS == 0 → línia
// de la graella principal; si no, sub-graella.
for (int i = 1; i < SUB_VERTS; i++) {
const float X = zona.x + (static_cast<float>(i) * SUB_W);
const bool IS_MAIN = (i % Defaults::Playfield::SUBDIVISIONS) == 0;
const float BRIGHTNESS = IS_MAIN
? Defaults::Playfield::GRID_BRIGHTNESS
: Defaults::Playfield::SUBGRID_BRIGHTNESS;
verticals.push_back(Line{
.start = {.x = X, .y = zona.y},
.end = {.x = X, .y = zona.y + zona.h},
.brightness = BRIGHTNESS,
.slot = 0});
}
// Horitzontals: posicions j ∈ [1, SUB_HORIZ-1]. Mateix criteri main/sub.
for (int j = 1; j < SUB_HORIZ; j++) {
const float Y = zona.y + (static_cast<float>(j) * SUB_H);
const bool IS_MAIN = (j % Defaults::Playfield::SUBDIVISIONS) == 0;
const float BRIGHTNESS = IS_MAIN
? Defaults::Playfield::GRID_BRIGHTNESS
: Defaults::Playfield::SUBGRID_BRIGHTNESS;
horizontals.push_back(Line{
.start = {.x = zona.x, .y = Y},
.end = {.x = zona.x + zona.w, .y = Y},
.brightness = BRIGHTNESS,
.slot = 0});
}
// Verticals ja venen ordenats per x ascendent (loop sobre i). Assignem slots 0..N-1.
// Horitzontals ja venen ordenats per y ascendent. Assignem slots N..total-1.
lines_.clear();
lines_.reserve(verticals.size() + horizontals.size());
int slot = 0;
for (auto& line : verticals) {
line.slot = slot++;
lines_.push_back(line);
}
for (auto& line : horizontals) {
line.slot = slot++;
lines_.push_back(line);
}
}
auto Playfield::computeLineProgress(int slot) const -> float {
const float LINE_START = static_cast<float>(slot) * spawn_interval_s_;
const float LINE_ELAPSED = elapsed_s_ - LINE_START;
return std::clamp(LINE_ELAPSED / Defaults::Playfield::LINE_GROWTH_DURATION_S, 0.0F, 1.0F);
}
void Playfield::draw() const {
for (const auto& line : lines_) {
const float P = computeLineProgress(line.slot);
if (P <= 0.0F) {
continue;
}
const float END_X = line.start.x + ((line.end.x - line.start.x) * P);
const float END_Y = line.start.y + ((line.end.y - line.start.y) * P);
Rendering::linea(
renderer_,
static_cast<int>(line.start.x),
static_cast<int>(line.start.y),
static_cast<int>(END_X),
static_cast<int>(END_Y),
line.brightness);
}
}
} // namespace Graphics