refactor: extreure helpers per reduir complexitat cognitiva (tidy net)
This commit is contained in:
+218
-235
@@ -114,258 +114,241 @@ namespace Overlay {
|
||||
}
|
||||
}
|
||||
|
||||
static void updateNotifFsm(Notification& notif, float dt) {
|
||||
switch (notif.status) {
|
||||
case Status::RISING:
|
||||
notif.anim += SLIDE_SPEED * dt;
|
||||
if (notif.anim >= 1.0F) {
|
||||
notif.anim = 1.0F;
|
||||
notif.status = Status::STAY;
|
||||
notif.timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case Status::STAY:
|
||||
notif.timer += dt;
|
||||
if (notif.timer >= notif.duration) {
|
||||
notif.status = Status::VANISHING;
|
||||
}
|
||||
break;
|
||||
case Status::VANISHING:
|
||||
notif.anim -= SLIDE_SPEED * dt;
|
||||
if (notif.anim <= 0.0F) {
|
||||
notif.status = Status::FINISHED;
|
||||
}
|
||||
break;
|
||||
case Status::FINISHED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void computeNotifBoxPos(const Notification& notif, int& box_x, int& box_y) {
|
||||
switch (notif.pos) {
|
||||
case NotifPosition::TOP_LEFT_SLIDE:
|
||||
box_x = NOTIF_MARGIN_X - static_cast<int>((1.0F - notif.anim) * (notif.box_w + NOTIF_MARGIN_X));
|
||||
box_y = NOTIF_MARGIN_Y;
|
||||
break;
|
||||
case NotifPosition::TOP_CENTER_DROP:
|
||||
box_x = (SCREEN_W - notif.box_w) / 2;
|
||||
box_y = NOTIF_MARGIN_Y - static_cast<int>((1.0F - notif.anim) * (notif.box_h + NOTIF_MARGIN_Y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void drawNotifTextLine(Uint32* pixel_data, const std::string& line, int line_x, int line_y, const Notification& notif) {
|
||||
if (notif.style == NotifStyle::SHADOW) {
|
||||
font->draw(pixel_data, line_x + 1, line_y + 1, line.c_str(), notif.accent_color);
|
||||
} else if (notif.style == NotifStyle::OUTLINE) {
|
||||
font->draw(pixel_data, line_x, line_y - 1, line.c_str(), notif.accent_color);
|
||||
font->draw(pixel_data, line_x, line_y + 1, line.c_str(), notif.accent_color);
|
||||
font->draw(pixel_data, line_x - 1, line_y, line.c_str(), notif.accent_color);
|
||||
font->draw(pixel_data, line_x + 1, line_y, line.c_str(), notif.accent_color);
|
||||
}
|
||||
font->draw(pixel_data, line_x, line_y, line.c_str(), notif.text_color);
|
||||
}
|
||||
|
||||
static void renderOneNotification(Uint32* pixel_data, const Notification& notif) {
|
||||
int box_x = 0;
|
||||
int box_y = 0;
|
||||
computeNotifBoxPos(notif, box_x, box_y);
|
||||
if (notif.style == NotifStyle::BOX) {
|
||||
drawRect(pixel_data, box_x, box_y, notif.box_w, notif.box_h, notif.accent_color);
|
||||
}
|
||||
const int LINE_H = font->charHeight();
|
||||
int line_y = box_y + NOTIF_PADDING_V;
|
||||
for (const auto& line : notif.lines) {
|
||||
const int LINE_W = font->width(line.c_str());
|
||||
const int LINE_X = box_x + ((notif.box_w - LINE_W) / 2);
|
||||
drawNotifTextLine(pixel_data, line, LINE_X, line_y, notif);
|
||||
line_y += LINE_H + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void updateRenderInfoFsm(float dt) {
|
||||
const auto DESIRED = Options::render_info.position;
|
||||
if (DESIRED == info_visible_pos) {
|
||||
if (info_anim < 1.0F) {
|
||||
info_anim = std::min(info_anim + (INFO_SLIDE_SPEED * dt), 1.0F);
|
||||
}
|
||||
} else if (info_visible_pos == Options::RenderInfoPosition::OFF) {
|
||||
info_visible_pos = DESIRED;
|
||||
info_anim = 0.0F;
|
||||
} else {
|
||||
info_anim -= INFO_SLIDE_SPEED * dt;
|
||||
if (info_anim <= 0.0F) {
|
||||
info_anim = 0.0F;
|
||||
info_visible_pos = DESIRED;
|
||||
}
|
||||
}
|
||||
for (auto& seg : info_segments) {
|
||||
const float TARGET = seg.visible ? 1.0F : 0.0F;
|
||||
if (seg.anim < TARGET) {
|
||||
seg.anim = std::min(seg.anim + (SEG_SPEED * dt), TARGET);
|
||||
} else if (seg.anim > TARGET) {
|
||||
seg.anim = std::max(seg.anim - (SEG_SPEED * dt), TARGET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static auto computeInfoTotalWidth(int digit_cell) -> float {
|
||||
float total_w = 0.0F;
|
||||
for (const auto& seg : info_segments) {
|
||||
if (seg.anim > 0.0F && !seg.text.empty()) {
|
||||
const int W = seg.mono_digits
|
||||
? font->widthMonoDigits(seg.text.c_str(), digit_cell)
|
||||
: font->width(seg.text.c_str());
|
||||
total_w += static_cast<float>(W) * Easing::outQuad(seg.anim);
|
||||
}
|
||||
}
|
||||
return total_w;
|
||||
}
|
||||
|
||||
static void drawInfoSegment(Uint32* pixel_data, const InfoSegment& seg, int xi, int info_y, int digit_cell) {
|
||||
if (seg.mono_digits) {
|
||||
font->drawMonoDigits(pixel_data, xi + 1, info_y + 1, seg.text.c_str(), Options::render_info.shadow_color, digit_cell);
|
||||
font->drawMonoDigits(pixel_data, xi, info_y, seg.text.c_str(), Options::render_info.text_color, digit_cell);
|
||||
} else {
|
||||
font->draw(pixel_data, xi + 1, info_y + 1, seg.text.c_str(), Options::render_info.shadow_color);
|
||||
font->draw(pixel_data, xi, info_y, seg.text.c_str(), Options::render_info.text_color);
|
||||
}
|
||||
}
|
||||
|
||||
static void renderRenderInfo(Uint32* pixel_data) {
|
||||
if (info_visible_pos == Options::RenderInfoPosition::OFF || info_anim <= 0.0F) {
|
||||
return;
|
||||
}
|
||||
const int DIGIT_CELL = font->charBoxWidth() - 1;
|
||||
const float TOTAL_W = computeInfoTotalWidth(DIGIT_CELL);
|
||||
if (TOTAL_W <= 0.0F) {
|
||||
return;
|
||||
}
|
||||
const float EASED_Y = Easing::outQuad(info_anim);
|
||||
const int CH = font->charHeight();
|
||||
const int FINAL_Y = (info_visible_pos == Options::RenderInfoPosition::TOP) ? 1 : SCREEN_H - CH - 1;
|
||||
const int START_Y = (info_visible_pos == Options::RenderInfoPosition::TOP) ? -CH - 1 : SCREEN_H;
|
||||
const int INFO_Y = START_Y + static_cast<int>(static_cast<float>(FINAL_Y - START_Y) * EASED_Y);
|
||||
float cur_x = (SCREEN_W - TOTAL_W) / 2.0F;
|
||||
for (const auto& seg : info_segments) {
|
||||
if (seg.anim <= 0.01F || seg.text.empty()) {
|
||||
continue;
|
||||
}
|
||||
const int XI = static_cast<int>(cur_x);
|
||||
const int SEG_W = seg.mono_digits
|
||||
? font->widthMonoDigits(seg.text.c_str(), DIGIT_CELL)
|
||||
: font->width(seg.text.c_str());
|
||||
drawInfoSegment(pixel_data, seg, XI, INFO_Y, DIGIT_CELL);
|
||||
cur_x += static_cast<float>(SEG_W) * Easing::outQuad(seg.anim);
|
||||
}
|
||||
}
|
||||
|
||||
static void renderPauseIndicator(Uint32* pixel_data) {
|
||||
if ((Director::get() == nullptr) || !Director::get()->isPaused()) {
|
||||
return;
|
||||
}
|
||||
const char* pause_text = Locale::get("notifications.pause");
|
||||
const int W = font->width(pause_text);
|
||||
const int X = SCREEN_W - W - 4;
|
||||
const int Y = 4;
|
||||
font->draw(pixel_data, X, Y - 1, pause_text, 0xFFFFFFFF);
|
||||
font->draw(pixel_data, X, Y + 1, pause_text, 0xFFFFFFFF);
|
||||
font->draw(pixel_data, X - 1, Y, pause_text, 0xFFFFFFFF);
|
||||
font->draw(pixel_data, X + 1, Y, pause_text, 0xFFFFFFFF);
|
||||
font->draw(pixel_data, X, Y, pause_text, 0xFF0000FF);
|
||||
}
|
||||
|
||||
static void emitCreditsLines(const char* role_key, const char* name_key) {
|
||||
showNotification(
|
||||
{std::string(Locale::get(role_key)), std::string(Locale::get(name_key))},
|
||||
CREDITS_HOLD,
|
||||
NotifPosition::TOP_CENTER_DROP,
|
||||
NotifStyle::OUTLINE,
|
||||
CREDITS_BG,
|
||||
CREDITS_FG);
|
||||
}
|
||||
|
||||
static void advanceCredits(float dt) {
|
||||
if (credits_phase == CreditsPhase::IDLE) {
|
||||
return;
|
||||
}
|
||||
credits_timer += dt;
|
||||
switch (credits_phase) {
|
||||
case CreditsPhase::DELAY:
|
||||
if (credits_timer >= CREDITS_DELAY) {
|
||||
emitCreditsLines("credits.port_role", "credits.port_name");
|
||||
credits_phase = CreditsPhase::PLAYING_1;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::PLAYING_1:
|
||||
if (notifications.empty()) {
|
||||
credits_phase = CreditsPhase::GAP;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::GAP:
|
||||
if (credits_timer >= CREDITS_GAP) {
|
||||
emitCreditsLines("credits.modern_role", "credits.modern_name");
|
||||
credits_phase = CreditsPhase::PLAYING_2;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::PLAYING_2:
|
||||
if (notifications.empty()) {
|
||||
credits_phase = CreditsPhase::IDLE;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::IDLE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void render(Uint32* pixel_data) {
|
||||
if (!font || (pixel_data == nullptr)) {
|
||||
return;
|
||||
}
|
||||
const Uint32 NOW = SDL_GetTicks();
|
||||
const float DT = static_cast<float>(NOW - last_ticks) / 1000.0F;
|
||||
last_ticks = NOW;
|
||||
|
||||
// Calcula delta time
|
||||
Uint32 now = SDL_GetTicks();
|
||||
float dt = static_cast<float>(now - last_ticks) / 1000.0F;
|
||||
last_ticks = now;
|
||||
|
||||
// Actualitza i pinta cada notificació
|
||||
for (auto& notif : notifications) {
|
||||
switch (notif.status) {
|
||||
case Status::RISING:
|
||||
notif.anim += SLIDE_SPEED * dt;
|
||||
if (notif.anim >= 1.0F) {
|
||||
notif.anim = 1.0F;
|
||||
notif.status = Status::STAY;
|
||||
notif.timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status::STAY:
|
||||
notif.timer += dt;
|
||||
if (notif.timer >= notif.duration) {
|
||||
notif.status = Status::VANISHING;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status::VANISHING:
|
||||
notif.anim -= SLIDE_SPEED * dt;
|
||||
if (notif.anim <= 0.0F) {
|
||||
notif.status = Status::FINISHED;
|
||||
}
|
||||
break;
|
||||
|
||||
case Status::FINISHED:
|
||||
break;
|
||||
}
|
||||
|
||||
if (notif.status == Status::FINISHED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Posició segons el tipus
|
||||
int box_x = 0;
|
||||
int box_y = 0;
|
||||
switch (notif.pos) {
|
||||
case NotifPosition::TOP_LEFT_SLIDE: {
|
||||
int target_x = NOTIF_MARGIN_X;
|
||||
int target_y = NOTIF_MARGIN_Y;
|
||||
box_x = target_x - static_cast<int>((1.0F - notif.anim) * (notif.box_w + NOTIF_MARGIN_X));
|
||||
box_y = target_y;
|
||||
break;
|
||||
}
|
||||
case NotifPosition::TOP_CENTER_DROP: {
|
||||
int target_y = NOTIF_MARGIN_Y;
|
||||
box_x = (SCREEN_W - notif.box_w) / 2;
|
||||
// Baixa des de sobre de la pantalla fins a target_y
|
||||
box_y = target_y - static_cast<int>((1.0F - notif.anim) * (notif.box_h + NOTIF_MARGIN_Y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Pinta fons (si BOX)
|
||||
if (notif.style == NotifStyle::BOX) {
|
||||
drawRect(pixel_data, box_x, box_y, notif.box_w, notif.box_h, notif.accent_color);
|
||||
}
|
||||
|
||||
// Pinta el text línia a línia (amb ombra o contorn segons style)
|
||||
int line_h = font->charHeight();
|
||||
int line_y = box_y + NOTIF_PADDING_V;
|
||||
for (const auto& line : notif.lines) {
|
||||
int line_w = font->width(line.c_str());
|
||||
int line_x = box_x + ((notif.box_w - line_w) / 2); // centrat dins la caixa
|
||||
if (notif.style == NotifStyle::SHADOW) {
|
||||
font->draw(pixel_data, line_x + 1, line_y + 1, line.c_str(), notif.accent_color);
|
||||
} else if (notif.style == NotifStyle::OUTLINE) {
|
||||
// Contorn 4-direccional (N, S, E, W)
|
||||
font->draw(pixel_data, line_x, line_y - 1, line.c_str(), notif.accent_color);
|
||||
font->draw(pixel_data, line_x, line_y + 1, line.c_str(), notif.accent_color);
|
||||
font->draw(pixel_data, line_x - 1, line_y, line.c_str(), notif.accent_color);
|
||||
font->draw(pixel_data, line_x + 1, line_y, line.c_str(), notif.accent_color);
|
||||
}
|
||||
font->draw(pixel_data, line_x, line_y, line.c_str(), notif.text_color);
|
||||
line_y += line_h + 1;
|
||||
updateNotifFsm(notif, DT);
|
||||
if (notif.status != Status::FINISHED) {
|
||||
renderOneNotification(pixel_data, notif);
|
||||
}
|
||||
}
|
||||
|
||||
// Render info (FPS, driver, shader) — animat amb slide vertical
|
||||
// State machine: visible_pos s'actualitza cap a DESIRED quan anim arriba a 0
|
||||
{
|
||||
const auto DESIRED = Options::render_info.position;
|
||||
if (DESIRED == info_visible_pos) {
|
||||
// Mateix lloc: entra fins a 1
|
||||
if (info_anim < 1.0F) {
|
||||
info_anim += INFO_SLIDE_SPEED * dt;
|
||||
info_anim = std::min(info_anim, 1.0F);
|
||||
}
|
||||
} else {
|
||||
// Canvi: si visible_pos està OFF, commuta directament
|
||||
if (info_visible_pos == Options::RenderInfoPosition::OFF) {
|
||||
info_visible_pos = DESIRED;
|
||||
info_anim = 0.0F;
|
||||
} else {
|
||||
// Ix del lloc actual
|
||||
info_anim -= INFO_SLIDE_SPEED * dt;
|
||||
if (info_anim <= 0.0F) {
|
||||
info_anim = 0.0F;
|
||||
info_visible_pos = DESIRED;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateRenderInfoFsm(DT);
|
||||
renderRenderInfo(pixel_data);
|
||||
|
||||
// Actualitza animacions individuals dels segments
|
||||
for (auto& seg : info_segments) {
|
||||
float target = seg.visible ? 1.0F : 0.0F;
|
||||
if (seg.anim < target) {
|
||||
seg.anim += SEG_SPEED * dt;
|
||||
seg.anim = std::min(seg.anim, target);
|
||||
} else if (seg.anim > target) {
|
||||
seg.anim -= SEG_SPEED * dt;
|
||||
seg.anim = std::max(seg.anim, target);
|
||||
}
|
||||
}
|
||||
|
||||
// Render si hi ha alguna cosa visible
|
||||
if (info_visible_pos != Options::RenderInfoPosition::OFF && info_anim > 0.0F) {
|
||||
const int DIGIT_CELL = font->charBoxWidth() - 1; // amplada uniforme per dígit
|
||||
|
||||
// Calcula amplada total interpolant cada segment per la seva anim
|
||||
float total_w = 0.0F;
|
||||
for (const auto& seg : info_segments) {
|
||||
if (seg.anim > 0.0F && !seg.text.empty()) {
|
||||
int w = seg.mono_digits
|
||||
? font->widthMonoDigits(seg.text.c_str(), DIGIT_CELL)
|
||||
: font->width(seg.text.c_str());
|
||||
total_w += w * Easing::outQuad(seg.anim);
|
||||
}
|
||||
}
|
||||
if (total_w > 0.0F) {
|
||||
float eased_y = Easing::outQuad(info_anim);
|
||||
int ch = font->charHeight();
|
||||
int final_y;
|
||||
int start_y;
|
||||
if (info_visible_pos == Options::RenderInfoPosition::TOP) {
|
||||
final_y = 1;
|
||||
start_y = -ch - 1;
|
||||
} else {
|
||||
final_y = SCREEN_H - ch - 1;
|
||||
start_y = SCREEN_H;
|
||||
}
|
||||
int info_y = start_y + static_cast<int>((final_y - start_y) * eased_y);
|
||||
|
||||
// Dibuixa cada segment en la seva posició x acumulada
|
||||
float cur_x = (SCREEN_W - total_w) / 2.0F;
|
||||
for (const auto& seg : info_segments) {
|
||||
if (seg.anim > 0.01F && !seg.text.empty()) {
|
||||
int xi = static_cast<int>(cur_x);
|
||||
int seg_w = seg.mono_digits
|
||||
? font->widthMonoDigits(seg.text.c_str(), DIGIT_CELL)
|
||||
: font->width(seg.text.c_str());
|
||||
if (seg.mono_digits) {
|
||||
font->drawMonoDigits(pixel_data, xi + 1, info_y + 1, seg.text.c_str(), Options::render_info.shadow_color, DIGIT_CELL);
|
||||
font->drawMonoDigits(pixel_data, xi, info_y, seg.text.c_str(), Options::render_info.text_color, DIGIT_CELL);
|
||||
} else {
|
||||
font->draw(pixel_data, xi + 1, info_y + 1, seg.text.c_str(), Options::render_info.shadow_color);
|
||||
font->draw(pixel_data, xi, info_y, seg.text.c_str(), Options::render_info.text_color);
|
||||
}
|
||||
cur_x += seg_w * Easing::outQuad(seg.anim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Elimina les acabades
|
||||
std::erase_if(notifications, [](const Notification& n) { return n.status == Status::FINISHED; });
|
||||
|
||||
// Si la notificació d'ESC ha desaparegut, reseteja l'estat
|
||||
if (esc_waiting && notifications.empty()) {
|
||||
esc_waiting = false;
|
||||
}
|
||||
|
||||
// Indicador de pausa persistent (cantó superior dret)
|
||||
if ((Director::get() != nullptr) && Director::get()->isPaused()) {
|
||||
const char* pause_text = Locale::get("notifications.pause");
|
||||
int w = font->width(pause_text);
|
||||
int x = SCREEN_W - w - 4;
|
||||
int y = 4;
|
||||
// Contorn blanc 4-direccional
|
||||
font->draw(pixel_data, x, y - 1, pause_text, 0xFFFFFFFF);
|
||||
font->draw(pixel_data, x, y + 1, pause_text, 0xFFFFFFFF);
|
||||
font->draw(pixel_data, x - 1, y, pause_text, 0xFFFFFFFF);
|
||||
font->draw(pixel_data, x + 1, y, pause_text, 0xFFFFFFFF);
|
||||
// Text en roig
|
||||
font->draw(pixel_data, x, y, pause_text, 0xFF0000FF);
|
||||
}
|
||||
renderPauseIndicator(pixel_data);
|
||||
advanceCredits(DT);
|
||||
|
||||
// Crèdits seqüencials — dispara notificacions TOP_CENTER_DROP una darrere l'altra.
|
||||
if (credits_phase != CreditsPhase::IDLE) {
|
||||
credits_timer += dt;
|
||||
switch (credits_phase) {
|
||||
case CreditsPhase::DELAY:
|
||||
if (credits_timer >= CREDITS_DELAY) {
|
||||
showNotification(
|
||||
{std::string(Locale::get("credits.port_role")),
|
||||
std::string(Locale::get("credits.port_name"))},
|
||||
CREDITS_HOLD,
|
||||
NotifPosition::TOP_CENTER_DROP,
|
||||
NotifStyle::OUTLINE,
|
||||
CREDITS_BG,
|
||||
CREDITS_FG);
|
||||
credits_phase = CreditsPhase::PLAYING_1;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::PLAYING_1:
|
||||
if (notifications.empty()) {
|
||||
credits_phase = CreditsPhase::GAP;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::GAP:
|
||||
if (credits_timer >= CREDITS_GAP) {
|
||||
showNotification(
|
||||
{std::string(Locale::get("credits.modern_role")),
|
||||
std::string(Locale::get("credits.modern_name"))},
|
||||
CREDITS_HOLD,
|
||||
NotifPosition::TOP_CENTER_DROP,
|
||||
NotifStyle::OUTLINE,
|
||||
CREDITS_BG,
|
||||
CREDITS_FG);
|
||||
credits_phase = CreditsPhase::PLAYING_2;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::PLAYING_2:
|
||||
if (notifications.empty()) {
|
||||
credits_phase = CreditsPhase::IDLE;
|
||||
credits_timer = 0.0F;
|
||||
}
|
||||
break;
|
||||
case CreditsPhase::IDLE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Neteja notificacions finalitzades
|
||||
std::erase_if(notifications, [](const Notification& n) { return n.status == Status::FINISHED; });
|
||||
|
||||
// Menú flotant per damunt de tot (isVisible inclou l'animació de tancament)
|
||||
if (Menu::isVisible()) {
|
||||
Menu::render(pixel_data);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user