Skip to main content

cuqueclicker_lib/ui/
stats.rs

1use ratatui::{prelude::*, widgets::*};
2
3use crate::format;
4use crate::game::achievement;
5use crate::game::state::{GameState, TICK_HZ};
6use crate::i18n::t;
7
8// Per-variant tints for the powerup-breakdown rows. Match the border
9// channel hues in `src/ui/border.rs` so the stats panel and the in-game
10// HUD pulse colors are visually coherent — a player who saw red for
11// Frenzy on the title border should see red for "Frenzy caught" here too.
12const LUCKY_FG: Color = Color::Rgb(255, 215, 0); // border LUCKY_TINT
13const FRENZY_FG: Color = Color::Rgb(255, 80, 80); // border FRENZY_TINT-ish (legible)
14const BUFF_FG: Color = Color::Rgb(220, 140, 255); // border BUFF_TINT
15const GREEN_FG: Color = Color::Rgb(120, 230, 140); // border GREEN_COIN_TINT
16
17pub fn draw(frame: &mut Frame, area: Rect, state: &GameState) {
18    let lang = t();
19    let session_secs = state.session_ticks / TICK_HZ as u64;
20    let total_secs = state.total_play_ticks / TICK_HZ as u64;
21    let unlocked = state.achievements_earned.len();
22    let total = achievement::count();
23
24    // Default value tint for non-variant rows.
25    let neutral = Color::Rgb(240, 220, 180);
26
27    // (label, value, value_color). Variant-specific rows tint the value to
28    // match the in-game border channel for that powerup.
29    let rows: [(&str, String, Color); 12] = [
30        (
31            lang.stat_session_time,
32            format::duration(session_secs),
33            neutral,
34        ),
35        (lang.stat_total_time, format::duration(total_secs), neutral),
36        (
37            lang.stat_total_clicks,
38            format::big(state.total_clicks as f64),
39            neutral,
40        ),
41        (
42            lang.stat_lifetime_cuques,
43            format::big_mag(state.lifetime_cuques),
44            neutral,
45        ),
46        (lang.stat_best_fps, format::big_mag(state.best_fps), neutral),
47        (
48            lang.stat_fingerers_owned,
49            format::big(state.fingerers_owned_total() as f64),
50            neutral,
51        ),
52        (
53            lang.stat_golden_caught,
54            format::big(state.golden_caught as f64),
55            neutral,
56        ),
57        (
58            lang.stat_lucky_caught,
59            format::big(state.lucky_caught as f64),
60            LUCKY_FG,
61        ),
62        (
63            lang.stat_frenzy_caught,
64            format::big(state.frenzy_caught as f64),
65            FRENZY_FG,
66        ),
67        (
68            lang.stat_buff_caught,
69            format::big(state.buff_caught as f64),
70            BUFF_FG,
71        ),
72        (
73            lang.stat_green_coin_caught,
74            format::big(state.green_coin_caught as f64),
75            GREEN_FG,
76        ),
77        (
78            lang.stat_achievements,
79            format!("{} / {}", unlocked, total),
80            neutral,
81        ),
82    ];
83
84    let label_w = rows
85        .iter()
86        .map(|(label, _, _)| label.chars().count())
87        .max()
88        .unwrap_or(0);
89
90    let lines: Vec<Line> = rows
91        .iter()
92        .map(|(label, value, value_fg)| {
93            Line::from(vec![
94                Span::styled(
95                    format!("{:<width$}  ", label, width = label_w),
96                    Style::default().fg(Color::DarkGray),
97                ),
98                Span::styled(
99                    value.clone(),
100                    Style::default().fg(*value_fg).add_modifier(Modifier::BOLD),
101                ),
102            ])
103        })
104        .collect();
105
106    let p = Paragraph::new(lines).block(Block::bordered().title(lang.stats_title));
107    frame.render_widget(p, area);
108}