Statistics with a chart at the end of a game, 64 achievements kept locally and on the server
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
193b199875
commit
9931ca9b54
37 files changed
+1833
-4
No files matched your search
@@ -0,0 +1,70 @@
|
||||
// The achievements page: every achievement with its badge (bronze, silver, gold, ... in the
|
||||
// colour of its tier), name, description and the progress towards it. Hidden ones show "???"
|
||||
// until they are unlocked.
|
||||
|
||||
use super::*;
|
||||
use crate::achievements::{all, texts, TIERS};
|
||||
|
||||
const TIER_COL: [(f32, f32, f32); 5] = [(0.72, 0.45, 0.25), (0.75, 0.78, 0.85), (0.95, 0.78, 0.25), (0.55, 0.9, 1.0), (0.85, 0.5, 1.0)];
|
||||
|
||||
impl Menu {
|
||||
pub(super) fn draw_achievements(&mut self, gfx: &Gfx) {
|
||||
let sw = sw();
|
||||
let sh = sh();
|
||||
let cols = 70.min((sw / CW()) as i32 - 2);
|
||||
let rows = 40.min((sh / CH()) as i32 - 2);
|
||||
let x = grid_center(sw, cols as f32 * CW()).max(0.0);
|
||||
let y = grid_center_y(sh, rows as f32 * CH()).max(0.0);
|
||||
gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &tr("menu.achievements"), FRAME_HI);
|
||||
let list = all();
|
||||
let done = list.iter().filter(|a| self.profile.unlocked.contains_key(&a.0)).count();
|
||||
let head = trf("ach.count", &[done.to_string(), list.len().to_string(), crate::achievements::fmt_count(self.profile.total("minutes"))]);
|
||||
gfx.text(&head, x + CW() * 3.0, y + CH() * 1.6, S(), GOLD);
|
||||
let top = y + CH() * 3.5;
|
||||
let bottom = y + (rows - 4) as f32 * CH();
|
||||
let row_h = CH() * 3.2;
|
||||
self.rows_visible = (((bottom - top) / row_h).floor() as usize).max(1);
|
||||
self.scroll = self.scroll.min(list.len().saturating_sub(self.rows_visible));
|
||||
let ix = x + CW() * 3.0;
|
||||
let w = (cols - 6) as f32 * CW();
|
||||
let maxc = ((w - CW() * 6.0) / (GW * SMALL())) as usize;
|
||||
for (k, (id, hidden, icon, tier)) in list.iter().enumerate().skip(self.scroll).take(self.rows_visible) {
|
||||
let ry = top + (k - self.scroll) as f32 * row_h;
|
||||
let got = self.profile.unlocked.contains_key(id);
|
||||
// the badge: a frame in the tier's colour around the icon (dark while locked)
|
||||
let (r, g, b) = TIER_COL[(*tier).min(4)];
|
||||
let bc = if got { Color::new(r, g, b, 1.0) } else { Color::new(0.25, 0.22, 0.22, 1.0) };
|
||||
let pic = CH() * 2.6;
|
||||
draw_rectangle(ix, ry, pic, pic, Color::new(0.08, 0.05, 0.05, 1.0));
|
||||
draw_rectangle_lines(ix, ry, pic, pic, 3.0, bc);
|
||||
let tint = if got { WHITE } else { Color::new(0.3, 0.3, 0.3, 1.0) };
|
||||
draw_texture_ex(&gfx.atlas, ix + 4.0, ry + 4.0, tint, DrawTextureParams { source: Some(crate::gfx::Gfx::cell(*icon)), dest_size: Some(vec2(pic - 8.0, pic - 8.0)), ..Default::default() });
|
||||
let tx = ix + pic + CW();
|
||||
let (name, desc) = if *hidden && !got { ("???".to_string(), tr("ach.hidden")) } else { texts(id) };
|
||||
gfx.text(&name, tx, ry + CH() * 0.1, S(), if got { GOLD } else { TXT });
|
||||
let d: String = desc.chars().take(maxc).collect();
|
||||
gfx.text(&d, tx, ry + CH() * 1.3, SMALL(), DIM);
|
||||
// progress towards a tier
|
||||
if let Some((cat, t)) = id.split_once('.') {
|
||||
if !got {
|
||||
let need = TIERS.iter().find(|x| x.0 == cat).and_then(|x| x.1.get(t.parse::<usize>().unwrap_or(1) - 1)).copied().unwrap_or(1.0);
|
||||
let have = self.profile.total(cat);
|
||||
let bar_x = x + (cols - 20) as f32 * CW();
|
||||
crate::ui::ascii_bar(gfx, bar_x, ry + CH() * 0.1, 10, (have / need).min(1.0) as f32, FRAME_HI, SMALL());
|
||||
let p = format!("{}/{}", crate::achievements::fmt_count(have.floor()), crate::achievements::fmt_count(need));
|
||||
gfx.text(&p, bar_x, ry + CH() * 1.3, SMALL(), DIM);
|
||||
}
|
||||
}
|
||||
}
|
||||
// scrolling and back
|
||||
let by = y + (rows - 3) as f32 * CH();
|
||||
let (mx, my) = mouse();
|
||||
let bw = 14.0 * CW();
|
||||
let items = [(Btn::Scroll(-1), "▲".to_string()), (Btn::Scroll(1), "▼".to_string()), (Btn::Back, tr("menu.back"))];
|
||||
for (k, (b, label)) in items.iter().enumerate() {
|
||||
let r = Rect::new(ix + k as f32 * (bw + CW()), by, bw, CH() * 2.0);
|
||||
button(gfx, r, label, r.contains(vec2(mx, my)), FRAME);
|
||||
self.buttons.push((r, *b));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user