Hall of the fallen: kills and causes of death, in the game and over all games in the profile
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
065fe80218
commit
c52fc4c4ae
28 files changed
+534
-5
No files matched your search
@@ -15,7 +15,13 @@ impl Menu {
|
||||
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 title = if self.ach_hall { tr("hall.title") } else { tr("menu.achievements") };
|
||||
gfx.window_titled(x, y, cols, rows, S(), true, FRAME_HI, Color::new(0.05, 0.03, 0.04, 0.97), &title, FRAME_HI);
|
||||
if self.ach_hall {
|
||||
self.draw_hall_page(gfx, x, y, cols, rows);
|
||||
self.ach_buttons(gfx, x, y, rows);
|
||||
return;
|
||||
}
|
||||
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"))]);
|
||||
@@ -57,14 +63,75 @@ impl Menu {
|
||||
}
|
||||
}
|
||||
// scrolling and back
|
||||
let _ = ix;
|
||||
self.ach_buttons(gfx, x, y, rows);
|
||||
}
|
||||
|
||||
// scrolling, the switch between achievements and the hall, back
|
||||
fn ach_buttons(&mut self, gfx: &Gfx, x: f32, y: f32, rows: i32) {
|
||||
let ix = x + CW() * 3.0;
|
||||
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"))];
|
||||
let switch = if self.ach_hall { tr("menu.achievements") } else { tr("hall.title") };
|
||||
let items = [(Btn::Scroll(-1), "▲".to_string()), (Btn::Scroll(1), "▼".to_string()), (Btn::AchHall, switch), (Btn::Back, tr("menu.back"))];
|
||||
let bw = bw.min((sw() - ix - CW() * 4.0) / 4.0 - CW());
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// the hall of the fallen over all games: newest first, two lines each
|
||||
fn draw_hall_page(&mut self, gfx: &Gfx, x: f32, y: f32, cols: i32, rows: i32) {
|
||||
let hall = &self.profile.hall;
|
||||
gfx.text(&trf("hall.count", &[hall.len().to_string()]), 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() * 2.4;
|
||||
self.rows_visible = (((bottom - top) / row_h).floor() as usize).max(1);
|
||||
self.scroll = self.scroll.min(hall.len().saturating_sub(self.rows_visible));
|
||||
let ix = x + CW() * 3.0;
|
||||
let maxc = (cols - 6) as usize;
|
||||
if hall.is_empty() {
|
||||
gfx.text(&tr("hall.empty"), ix, top, S(), DIM);
|
||||
}
|
||||
for (k, f) in hall.iter().rev().enumerate().skip(self.scroll).take(self.rows_visible) {
|
||||
let ry = top + (k - self.scroll) as f32 * row_h;
|
||||
let name = format!("{} ({})", crate::names::full_name(f.kind, f.name, f.level), trf("info.level", &[tr(crate::game::st(f.kind).name), f.level.to_string()]));
|
||||
gfx.text(&name.chars().take(maxc).collect::<String>(), ix, ry, S(), FRAME_HI);
|
||||
let kills = if f.kills > 0 { format!(" · {}", trf("hall.kills", &[f.kills.to_string()])) } else { String::new() };
|
||||
let line = format!("{}{} · {}", crate::names::cause(f.bane), kills, date(f.when));
|
||||
gfx.text(&line.chars().take(maxc * 2).collect::<String>(), ix + CW() * 2.0, ry + CH(), SMALL(), DIM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a unix time as a date (year-month-day)
|
||||
fn date(unix: u64) -> String {
|
||||
if unix == 0 {
|
||||
return String::new();
|
||||
}
|
||||
// days since 1970-01-01 to a civil date (Howard Hinnant's algorithm)
|
||||
let z = (unix / 86400) as i64 + 719468;
|
||||
let era = z.div_euclid(146097);
|
||||
let doe = z - era * 146097;
|
||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
let mp = (5 * doy + 2) / 153;
|
||||
let d = doy - (153 * mp + 2) / 5 + 1;
|
||||
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
||||
let y = yoe + era * 400 + if m <= 2 { 1 } else { 0 };
|
||||
format!("{:04}-{:02}-{:02}", y, m, d)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn dates_are_civil() {
|
||||
assert_eq!(super::date(0), "");
|
||||
assert_eq!(super::date(1_798_675_200), "2026-12-31");
|
||||
assert_eq!(super::date(1_709_164_800), "2024-02-29");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user