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, |term| {
88 setup_and_run_fruits_table_parameters(term, options);
89 });
90 }
91 Highs => {
92 info!("You choose to see the wall of fame, good luck beating them ! π―");
93 run_submenu_and_reset(terminal, selected, setup_and_run_highs_table_parameters);
94 }
95 Speed => {
96 info!("You choose to see the speeding information, good luck flash guy ! π’");
97 run_submenu_and_reset(terminal, selected, setup_and_run_speed_table_parameters);
98 }
99 Run => {
100 return MenuFlow::StartGame;
101 }
102 Doc => {
103 info!("You choose to see the doc information, good luck with books ! π");
104 run_submenu_and_reset(terminal, selected, setup_and_run_doc_table_parameters);
105 }
106 Main => {
107 display_main_menu(terminal, &SWITCH_MENUS_OPTION[*selected]);
108 }
109 }
110 MenuFlow::StayOnMainScreen
111}
112
113pub fn controls_main_switch_menu(
114 terminal: &mut DefaultTerminal,
115 options: &mut GameOptions,
116) -> bool {
117 let mut selected = 3;
118
119 loop {
120 display_main_menu(terminal, &SWITCH_MENUS_OPTION[selected]);
121
122 if event::poll(Duration::from_millis(SPEED_MOVING_SNAKE_SLEEP_TIME_MS / 2))
124 .expect("Error polling event")
125 {
126 let input = main_menu_event();
127
128 let flow = enter_menu_screen(&input, &mut selected, terminal, options);
129
130 match flow {
131 MenuFlow::StayOnMainScreen => {
132 }
135 MenuFlow::StartGame => return true,
136 MenuFlow::QuitGame => return false,
137 }
138 }
139 }
140}
141
142fn run_submenu_and_reset<F>(terminal: &mut DefaultTerminal, selected: &mut usize, f: F)
143where
144 F: FnOnce(&mut DefaultTerminal),
145{
146 f(terminal);
147 *selected = 3;
148}
149
150#[derive(PartialEq, Clone, Debug)]
151pub enum MainMenuInput {
152 Fruits,
153 Speed,
154 Start,
155 Parameters,
156 Doc,
157 Highs,
158 Main,
159 QuitGame,
160 Next,
161 Previous,
162 Enter,
163}
164
165#[must_use]
170pub fn main_menu_event() -> MainMenuInput {
171 if let event::Event::Key(key) = event::read().expect("Error reading key event") {
173 match key.kind {
174 KeyEventKind::Press => {
176 flush_input_buffer();
177 if START_KEYS.contains(&key.code) {
179 MainMenuInput::Start
180 } else if QUIT_KEYS.contains(&key.code) {
182 MainMenuInput::QuitGame
183 } else if PARAMETERS_KEYS.contains(&key.code) {
184 MainMenuInput::Parameters
185 } else if FRUITS_KEYS.contains(&key.code) {
186 MainMenuInput::Fruits
187 } else if VELOCITY_KEYS.contains(&key.code) {
188 MainMenuInput::Speed
189 } else if HELP_KEYS.contains(&key.code) {
190 MainMenuInput::Doc
191 } else if HIGH_SCORE_KEYS.contains(&key.code) {
192 MainMenuInput::Highs
193 } else if NEXT_KEYS.contains(&key.code) {
194 MainMenuInput::Next
195 } else if PREVIOUS_KEYS.contains(&key.code) {
196 MainMenuInput::Previous
197 } else if ENTER_KEYS.contains(&key.code) {
198 MainMenuInput::Enter
199 } else {
200 MainMenuInput::Main
201 }
202 }
203 _ => MainMenuInput::Main,
204 }
205 } else {
206 MainMenuInput::Main
207 }
208}
209fn flush_input_buffer() {
210 while event::poll(Duration::from_secs(0)).unwrap_or(false) {
211 let _ = crossterm::event::read(); }
213}