rsnaker/controls/
main_menu.rs1use crate::controls::playing_input::{QUIT_KEYS, START_KEYS};
2use crate::game_logic::game_options::GameOptions;
3use crate::game_logic::logger::log_configuration::update_log_level;
4use crate::graphics::menus::edge_snake::SPEED_MOVING_SNAKE_SLEEP_TIME_MS;
5use crate::graphics::menus::main_menu::SwitchMenu::{
6 Doc, Fruits, Highs, Main, Parameters, Run, Speed,
7};
8use crate::graphics::menus::main_menu::{display_main_menu, SwitchMenu};
9use crate::graphics::menus::retro_parameter_table::customized_with_doc::setup_and_run_doc_table_parameters;
10use crate::graphics::menus::retro_parameter_table::customized_with_edit::setup_and_run_cli_table_parameters;
11use crate::graphics::menus::retro_parameter_table::customized_with_fruits::setup_and_run_fruits_table_parameters;
12use crate::graphics::menus::retro_parameter_table::customized_with_highscore::setup_and_run_highs_table_parameters;
13use crate::graphics::menus::retro_parameter_table::customized_with_speed::setup_and_run_speed_table_parameters;
14use crossterm::event;
15use crossterm::event::{KeyCode, KeyEventKind};
16use ratatui::DefaultTerminal;
17use std::time::Duration;
18use tracing::{debug, error, info};
19
20const SWITCH_MENUS_OPTION: [SwitchMenu; 6] = [Highs, Fruits, Speed, Run, Parameters, Doc];
21const PARAMETERS_KEYS: [KeyCode; 2] = [KeyCode::Char('e'), KeyCode::Char('E')];
22const FRUITS_KEYS: [KeyCode; 2] = [KeyCode::Char('f'), KeyCode::Char('F')];
23const VELOCITY_KEYS: [KeyCode; 2] = [KeyCode::Char('s'), KeyCode::Char('P')];
24const HELP_KEYS: [KeyCode; 2] = [KeyCode::Char('d'), KeyCode::Char('D')];
25const HIGH_SCORE_KEYS: [KeyCode; 2] = [KeyCode::Char('h'), KeyCode::Char('H')];
26pub const NEXT_KEYS: [KeyCode; 2] = [KeyCode::Right, KeyCode::Up];
27pub const PREVIOUS_KEYS: [KeyCode; 3] = [KeyCode::Left, KeyCode::Backspace, KeyCode::Down];
28pub const ENTER_KEYS: [KeyCode; 2] = [KeyCode::Enter, KeyCode::End];
29enum MenuFlow {
38 StayOnMainScreen,
39 StartGame,
40 QuitGame,
41}
42
43fn enter_menu_screen(
44 input: &MainMenuInput,
45 selected: &mut usize,
46 terminal: &mut DefaultTerminal,
47 options: &mut GameOptions,
48) -> MenuFlow {
49 let to_display = match input {
51 MainMenuInput::Enter => SWITCH_MENUS_OPTION[*selected].clone(),
52 MainMenuInput::Parameters => Parameters,
53 MainMenuInput::Fruits => Fruits,
54 MainMenuInput::Speed => Speed,
55 MainMenuInput::Doc => Doc,
56 MainMenuInput::Highs => Highs,
57 MainMenuInput::Next => {
58 *selected = (*selected + 1) % SWITCH_MENUS_OPTION.len();
59 Main
60 }
61 MainMenuInput::Previous => {
62 *selected = (*selected + SWITCH_MENUS_OPTION.len() - 1) % SWITCH_MENUS_OPTION.len();
63 Main
64 }
65 MainMenuInput::QuitGame => {
66 error!("You choose to quit the game πΆβπ«οΈ ");
67 return MenuFlow::QuitGame;
68 }
69 MainMenuInput::Start => {
70 return MenuFlow::StartGame;
71 }
72 MainMenuInput::Main => Main,
73 };
74
75 match to_display {
76 Parameters => {
77 info!("You choose to edit game parameter, good luck engineer ! βοΈ");
78 run_submenu_and_reset(terminal, selected, |term| {
79 setup_and_run_cli_table_parameters(term, options);
80 });
81 update_log_level(options.log_level);
83 debug!(options = ?options, "Game options after editing");
84 }
85 Fruits => {
86 info!("You choose to see the fruit parameter, good luck they are tasty ! π");
87 run_submenu_and_reset(terminal, selected, setup_and_run_fruits_table_parameters);
88 }
89 Highs => {
90 info!("You choose to see the wall of fame, good luck beating them ! π―");
91 run_submenu_and_reset(terminal, selected, setup_and_run_highs_table_parameters);
92 }
93 Speed => {
94 info!("You choose to see the speeding information, good luck flash guy ! π’");
95 run_submenu_and_reset(terminal, selected, setup_and_run_speed_table_parameters);
96 }
97 Run => {
98 return MenuFlow::StartGame;
99 }
100 Doc => {
101 info!("You choose to see the doc information, good luck with books ! π");
102 run_submenu_and_reset(terminal, selected, setup_and_run_doc_table_parameters);
103 }
104 Main => {
105 display_main_menu(terminal, &SWITCH_MENUS_OPTION[*selected]);
106 }
107 }
108 MenuFlow::StayOnMainScreen
109}
110
111pub fn controls_main_switch_menu(
112 terminal: &mut DefaultTerminal,
113 options: &mut GameOptions,
114) -> bool {
115 let mut selected = 3;
116
117 loop {
118 display_main_menu(terminal, &SWITCH_MENUS_OPTION[selected]);
119
120 if event::poll(Duration::from_millis(SPEED_MOVING_SNAKE_SLEEP_TIME_MS / 2))
122 .expect("Error polling event")
123 {
124 let input = main_menu_event();
125
126 let flow = enter_menu_screen(&input, &mut selected, terminal, options);
127
128 match flow {
129 MenuFlow::StayOnMainScreen => {
130 }
133 MenuFlow::StartGame => return true,
134 MenuFlow::QuitGame => return false,
135 }
136 }
137 }
138}
139
140fn run_submenu_and_reset<F>(terminal: &mut DefaultTerminal, selected: &mut usize, f: F)
141where
142 F: FnOnce(&mut DefaultTerminal),
143{
144 f(terminal);
145 *selected = 3;
146}
147
148#[derive(PartialEq, Clone, Debug)]
149pub enum MainMenuInput {
150 Fruits,
151 Speed,
152 Start,
153 Parameters,
154 Doc,
155 Highs,
156 Main,
157 QuitGame,
158 Next,
159 Previous,
160 Enter,
161}
162
163#[must_use]
168pub fn main_menu_event() -> MainMenuInput {
169 if let event::Event::Key(key) = event::read().expect("Error reading key event") {
171 match key.kind {
172 KeyEventKind::Press => {
174 flush_input_buffer();
175 if START_KEYS.contains(&key.code) {
177 MainMenuInput::Start
178 } else if QUIT_KEYS.contains(&key.code) {
180 MainMenuInput::QuitGame
181 } else if PARAMETERS_KEYS.contains(&key.code) {
182 MainMenuInput::Parameters
183 } else if FRUITS_KEYS.contains(&key.code) {
184 MainMenuInput::Fruits
185 } else if VELOCITY_KEYS.contains(&key.code) {
186 MainMenuInput::Speed
187 } else if HELP_KEYS.contains(&key.code) {
188 MainMenuInput::Doc
189 } else if HIGH_SCORE_KEYS.contains(&key.code) {
190 MainMenuInput::Highs
191 } else if NEXT_KEYS.contains(&key.code) {
192 MainMenuInput::Next
193 } else if PREVIOUS_KEYS.contains(&key.code) {
194 MainMenuInput::Previous
195 } else if ENTER_KEYS.contains(&key.code) {
196 MainMenuInput::Enter
197 } else {
198 MainMenuInput::Main
199 }
200 }
201 _ => MainMenuInput::Main,
202 }
203 } else {
204 MainMenuInput::Main
205 }
206}
207fn flush_input_buffer() {
208 while event::poll(Duration::from_secs(0)).unwrap_or(false) {
209 let _ = crossterm::event::read(); }
211}