use std::io::{self, Write};
use crossterm::{
cursor::MoveTo,
event::{
self, Event, KeyCode, KeyEvent,
KeyEventKind::{Press, Repeat},
KeyModifiers,
},
style::Print,
terminal::{Clear, ClearType},
QueueableCommand,
};
use crate::{
application::{Application, Menu, MenuUpdate, ScoresEntry},
fmt_helpers::{fmt_duration, fmt_hertz, fmt_tetromino_counts},
};
impl<T: Write> Application<T> {
pub(in crate::application) fn run_menu_game_ended(
&mut self,
past_game: &ScoresEntry,
) -> io::Result<MenuUpdate> {
let ScoresEntry {
game_meta_data,
result,
time_elapsed,
lineclears,
points_scored,
pieces_locked,
fall_delay_reached,
lock_delay_reached,
} = past_game;
let selection = vec![
Menu::NewGame,
Menu::Settings,
Menu::ScoresAndReplays,
Menu::Quit,
];
if result.is_ok() && game_meta_data.title == "Puzzle" {
self.settings.new_game.experimental_mode_unlocked = true;
}
let mut selected = 0usize;
loop {
let w_main = Self::W_MAIN.into();
let (x_main, y_main) = Self::fetch_main_xy();
let y_selection = Self::H_MAIN / 5;
self.term
.queue(Clear(ClearType::All))?
.queue(MoveTo(x_main, y_main + y_selection))?
.queue(Print(format!(
"{:^w_main$}",
match result {
Ok(_stat) => format!("++ Game Completed ({}) ++", game_meta_data.title),
Err(cause) =>
format!("-- Game Over ({}) by: {cause:?} --", game_meta_data.title),
}
)))?
.queue(MoveTo(x_main, y_main + y_selection + 2))?
.queue(Print(format!("{:^w_main$}", "──────────────────────────")))?;
let mut stats = vec![
format!("Time elapsed: {}", fmt_duration(*time_elapsed)),
format!("Lines: {lineclears}"),
format!("Score: {points_scored}"),
format!("Pieces: {}", fmt_tetromino_counts(pieces_locked)),
format!("Gravity: {}", fmt_hertz(fall_delay_reached.as_hertz())),
];
if let Some(lock_delay_reached) = lock_delay_reached {
stats.push(format!(
"Lock delay: {}ms",
lock_delay_reached.saturating_duration().as_millis()
));
}
for (i, s) in stats.iter().enumerate() {
self.term
.queue(MoveTo(
x_main,
y_main + y_selection + 3 + u16::try_from(i).unwrap(),
))?
.queue(Print(format!("{s:^w_main$}")))?;
}
self.term
.queue(MoveTo(
x_main,
y_main + y_selection + 3 + u16::try_from(stats.len()).unwrap(),
))?
.queue(Print(format!("{:^w_main$}", "──────────────────────────")))?;
let names = selection
.iter()
.map(|menu| menu.to_string())
.collect::<Vec<_>>();
for (i, name) in names.into_iter().enumerate() {
self.term
.queue(MoveTo(
x_main,
y_main + y_selection + 3 + u16::try_from(stats.len() + 2 + i).unwrap(),
))?
.queue(Print(format!(
"{:^w_main$}",
if i == selected {
format!(">> {name} <<")
} else {
name
}
)))?;
}
self.term.flush()?;
match event::read()? {
Event::Key(KeyEvent {
code: KeyCode::Char('c' | 'C'),
modifiers: KeyModifiers::CONTROL,
kind: Press | Repeat,
state: _,
}) => break Ok(MenuUpdate::Push(Menu::Quit)),
Event::Key(KeyEvent {
code:
KeyCode::Esc
| KeyCode::Char('q' | 'Q')
| KeyCode::Backspace
| KeyCode::Char('b' | 'B'),
kind: Press,
..
}) => break Ok(MenuUpdate::Pop),
Event::Key(KeyEvent {
code: KeyCode::Enter | KeyCode::Char('e' | 'E'),
kind: Press,
..
}) => {
if !selection.is_empty() {
let menu = selection.into_iter().nth(selected).unwrap();
break Ok(MenuUpdate::Push(menu));
}
}
Event::Key(KeyEvent {
code: KeyCode::Up | KeyCode::Char('k' | 'K'),
kind: Press | Repeat,
..
}) => {
if !selection.is_empty() {
selected += selection.len() - 1;
}
}
Event::Key(KeyEvent {
code: KeyCode::Down | KeyCode::Char('j' | 'J'),
kind: Press | Repeat,
..
}) => {
if !selection.is_empty() {
selected += 1;
}
}
_ => {}
}
if !selection.is_empty() {
selected = selected.rem_euclid(selection.len());
}
}
}
}