rsnaker/controls/
main_menu.rs1use crate::controls::playing_input::{QUIT_KEYS, START_KEYS};
2use crate::game_logic::game_options::GameOptions;
3use crate::graphics::menus::main_menu::SwitchMenu::{
4 Doc, Fruits, Highs, Main, Parameters, Run, Speed,
5};
6use crate::graphics::menus::main_menu::{display_main_menu, SwitchMenu};
7use crate::graphics::menus::retro_parameter_table::customized_with_doc::setup_and_run_doc_table_parameters;
8use crate::graphics::menus::retro_parameter_table::customized_with_edit::setup_and_run_cli_table_parameters;
9use crate::graphics::menus::retro_parameter_table::customized_with_fruits::setup_and_run_fruits_table_parameters;
10use crate::graphics::menus::retro_parameter_table::customized_with_highscore::setup_and_run_highs_table_parameters;
11use crate::graphics::menus::retro_parameter_table::customized_with_speed::setup_and_run_speed_table_parameters;
12use crossterm::event;
13use crossterm::event::{KeyCode, KeyEventKind};
14use ratatui::DefaultTerminal;
15
16const SWITCH_MENUS_OPTION: [SwitchMenu; 6] = [Highs, Fruits, Speed, Run, Parameters, Doc];
17const PARAMETERS_KEYS: [KeyCode; 2] = [KeyCode::Char('e'), KeyCode::Char('E')];
18const FRUITS_KEYS: [KeyCode; 2] = [KeyCode::Char('f'), KeyCode::Char('F')];
19const VELOCITY_KEYS: [KeyCode; 2] = [KeyCode::Char('s'), KeyCode::Char('P')];
20const HELP_KEYS: [KeyCode; 2] = [KeyCode::Char('d'), KeyCode::Char('D')];
21const HIGH_SCORE_KEYS: [KeyCode; 2] = [KeyCode::Char('h'), KeyCode::Char('H')];
22pub const NEXT_KEYS: [KeyCode; 2] = [KeyCode::Right, KeyCode::Up];
23pub const PREVIOUS_KEYS: [KeyCode; 3] = [KeyCode::Left, KeyCode::Backspace, KeyCode::Down];
24pub const ENTER_KEYS: [KeyCode; 2] = [KeyCode::Enter, KeyCode::End];
25enum MenuFlow {
34 StayOnMainScreen,
35 StartGame,
36 QuitGame,
37}
38
39fn enter_menu_screen(
40 input: &MainMenuInput,
41 selected: &mut usize,
42 terminal: &mut DefaultTerminal,
43 options: &mut GameOptions,
44) -> MenuFlow {
45 let to_display = match input {
47 MainMenuInput::Enter => SWITCH_MENUS_OPTION[*selected].clone(),
48 MainMenuInput::Parameters => Parameters,
49 MainMenuInput::Fruits => Fruits,
50 MainMenuInput::Speed => Speed,
51 MainMenuInput::Doc => Doc,
52 MainMenuInput::Highs => Highs,
53 MainMenuInput::Next => {
54 *selected = (*selected + 1) % SWITCH_MENUS_OPTION.len();
55 Main
56 }
57 MainMenuInput::Previous => {
58 *selected = (*selected + SWITCH_MENUS_OPTION.len() - 1) % SWITCH_MENUS_OPTION.len();
59 Main
60 }
61 MainMenuInput::QuitGame => {
62 return MenuFlow::QuitGame;
63 }
64 MainMenuInput::Start => {
65 return MenuFlow::StartGame;
66 }
67 MainMenuInput::Main => Main,
68 };
69
70 match to_display {
71 Parameters => {
72 run_submenu_and_reset(terminal, selected, |term| {
73 setup_and_run_cli_table_parameters(term, options);
74 });
75 }
76 Fruits => run_submenu_and_reset(terminal, selected, setup_and_run_fruits_table_parameters),
77 Highs => run_submenu_and_reset(terminal, selected, setup_and_run_highs_table_parameters),
78 Speed => run_submenu_and_reset(terminal, selected, setup_and_run_speed_table_parameters),
79 Run => {
80 return MenuFlow::StartGame;
81 }
82 Doc => run_submenu_and_reset(terminal, selected, setup_and_run_doc_table_parameters),
83 Main => {
84 display_main_menu(terminal, &SWITCH_MENUS_OPTION[*selected]);
85 }
86 }
87 MenuFlow::StayOnMainScreen
88}
89
90pub fn controls_main_switch_menu(
91 terminal: &mut DefaultTerminal,
92 options: &mut GameOptions,
93) -> bool {
94 let mut selected = 3;
95 display_main_menu(terminal, &SWITCH_MENUS_OPTION[selected]);
96
97 loop {
98 let input = main_menu_event();
99
100 let flow = enter_menu_screen(&input, &mut selected, terminal, options);
101
102 match flow {
103 MenuFlow::StayOnMainScreen => {
104 display_main_menu(terminal, &SWITCH_MENUS_OPTION[selected]);
106 }
107 MenuFlow::StartGame => return true,
108 MenuFlow::QuitGame => return false,
109 }
110 }
111}
112
113fn run_submenu_and_reset<F>(terminal: &mut DefaultTerminal, selected: &mut usize, f: F)
114where
115 F: FnOnce(&mut DefaultTerminal),
116{
117 f(terminal);
118 *selected = 3;
119}
120
121#[derive(PartialEq, Clone, Debug)]
122pub enum MainMenuInput {
123 Fruits,
124 Speed,
125 Start,
126 Parameters,
127 Doc,
128 Highs,
129 Main,
130 QuitGame,
131 Next,
132 Previous,
133 Enter,
134}
135
136#[must_use]
141pub fn main_menu_event() -> MainMenuInput {
142 if let event::Event::Key(key) = event::read().expect("Error reading key event") {
144 match key.kind {
145 KeyEventKind::Press => {
147 flush_input_buffer();
148 if START_KEYS.contains(&key.code) {
150 MainMenuInput::Start
151 } else if QUIT_KEYS.contains(&key.code) {
153 MainMenuInput::QuitGame
154 } else if PARAMETERS_KEYS.contains(&key.code) {
155 MainMenuInput::Parameters
156 } else if FRUITS_KEYS.contains(&key.code) {
157 MainMenuInput::Fruits
158 } else if VELOCITY_KEYS.contains(&key.code) {
159 MainMenuInput::Speed
160 } else if HELP_KEYS.contains(&key.code) {
161 MainMenuInput::Doc
162 } else if HIGH_SCORE_KEYS.contains(&key.code) {
163 MainMenuInput::Highs
164 } else if NEXT_KEYS.contains(&key.code) {
165 MainMenuInput::Next
166 } else if PREVIOUS_KEYS.contains(&key.code) {
167 MainMenuInput::Previous
168 } else if ENTER_KEYS.contains(&key.code) {
169 MainMenuInput::Enter
170 } else {
171 MainMenuInput::Main
172 }
173 }
174 _ => MainMenuInput::Main,
175 }
176 } else {
177 MainMenuInput::Main
178 }
179}
180fn flush_input_buffer() {
181 while event::poll(std::time::Duration::from_secs(0)).unwrap_or(false) {
182 let _ = crossterm::event::read(); }
184}