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