feat(engine): empty-state copy + onboarding hints across panels

- Leaderboard empty state: replace single muted line with a two-tier
  "Be the first on the leaderboard." headline + body invite.
- Achievements panel: surface a first-launch hint above the grid until
  the player unlocks anything, so the greyed-out rows aren't context-free.
- Volume hotkeys ([/]): emit an InfoToastEvent with the new percentage so
  off-panel adjustments give visible feedback (previously silent).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-06 06:16:37 +00:00
parent cc635328be
commit 56e2e6f151
3 changed files with 33 additions and 2 deletions
@@ -474,9 +474,29 @@ fn spawn_achievements_screen(
..default()
};
let any_unlocked = records.iter().any(|r| r.unlocked);
let scrim = spawn_modal(commands, AchievementsScreen, Z_MODAL_PANEL, |card| {
spawn_modal_header(card, header, font_res);
// First-time hint — shown until the player has unlocked anything.
// The list itself describes individual rewards, but a top-level
// explanation gives newer players context for the otherwise dense
// greyed-out grid.
if !any_unlocked {
card.spawn((
Text::new(
"Complete games and try new modes to unlock achievements and rewards.",
),
TextFont {
font: font_res.map(|f| f.0.clone()).unwrap_or_default(),
font_size: TYPE_CAPTION,
..default()
},
TextColor(TEXT_SECONDARY),
));
}
// Scrollable body — the achievements list grows to ~19 rows which
// overflows the modal on the 800x600 minimum window. Wrapping the
// row list in an `Overflow::scroll_y()` Node with a constrained