use ratatui::{prelude::*, widgets::*};
use crate::game::achievement::ACHIEVEMENTS;
use crate::game::state::{GameState, TOAST_TICKS};
use crate::i18n::t;
pub fn draw(frame: &mut Frame, area: Rect, state: &GameState) {
let Some(id) = state.active_unlock_id.as_deref() else {
return;
};
if state.active_unlock_ticks == 0 {
return;
}
let Some(idx) = ACHIEVEMENTS.iter().position(|a| a.id == id) else {
return;
};
let lang = t();
let Some(name) = lang.achievement_names.get(idx).copied() else {
return;
};
let life = state.active_unlock_ticks as f32 / TOAST_TICKS as f32;
let strength = ease_in_out(life);
if strength <= 0.01 {
return;
}
let header_plain = header_plain();
let body_text = format!(" {name} ");
let inner_w = (header_plain.chars().count().max(body_text.chars().count()) + 2) as u16;
let w = (inner_w + 2).min(area.width.saturating_sub(2));
let h: u16 = 5;
if area.width < w + 2 || area.height < h + 2 {
return;
}
let x = area.x + (area.width.saturating_sub(w)) / 2;
let y = area.y + 2;
let rect = Rect {
x,
y,
width: w,
height: h,
};
let bg = Color::Rgb((30.0 * strength) as u8, (20.0 * strength) as u8, 0);
let bg_style = Style::default().bg(bg);
{
let buf = frame.buffer_mut();
for dy in 0..rect.height {
for dx in 0..rect.width {
let cx = rect.x + dx;
let cy = rect.y + dy;
if cx >= buf.area.x + buf.area.width || cy >= buf.area.y + buf.area.height {
continue;
}
let cell = &mut buf[(cx, cy)];
cell.set_char(' ');
cell.set_style(bg_style);
}
}
}
let border_fg = Color::Rgb(
(255.0 * strength) as u8,
(180.0 * strength) as u8,
(40.0 * strength) as u8,
);
let header_fg = border_fg;
let body_fg = Color::Rgb(
(255.0 * strength) as u8,
(230.0 * strength) as u8,
((180.0 * strength) as u8).max((60.0 * strength) as u8),
);
let header_style = Style::default()
.fg(header_fg)
.bg(bg)
.add_modifier(Modifier::BOLD);
let body_style = Style::default()
.fg(body_fg)
.bg(bg)
.add_modifier(Modifier::BOLD);
let block = Block::bordered()
.border_style(Style::default().fg(border_fg).bg(bg))
.style(bg_style);
let lines = vec![
Line::from(Span::styled(" ".repeat(w as usize), bg_style)),
Line::from(Span::styled(header_plain.to_string(), header_style)),
Line::from(Span::styled(body_text, body_style)),
Line::from(Span::styled(" ".repeat(w as usize), bg_style)),
];
let p = Paragraph::new(lines)
.alignment(Alignment::Center)
.block(block);
frame.render_widget(p, rect);
}
fn header_plain() -> &'static str {
" *** ACHIEVEMENT UNLOCKED *** "
}
fn smoothstep(t: f32) -> f32 {
let t = t.clamp(0.0, 1.0);
t * t * (3.0 - 2.0 * t)
}
fn ease_in_out(life: f32) -> f32 {
let life = life.clamp(0.0, 1.0);
let entry = smoothstep((1.0 - life) / 0.15);
let exit = smoothstep(life / 0.25);
entry.min(exit)
}