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 MenuFlow::StartGame => return true,
105 MenuFlow::QuitGame => return false,
106 }
107 }
108}
109
110fn run_submenu_and_reset<F>(terminal: &mut DefaultTerminal, selected: &mut usize, f: F)
111where
112 F: FnOnce(&mut DefaultTerminal),
113{
114 f(terminal);
115 *selected = 3;
116}
117
118#[derive(PartialEq, Clone, Debug)]
119pub enum MainMenuInput {
120 Fruits,
121 Speed,
122 Start,
123 Parameters,
124 Doc,
125 Highs,
126 Main,
127 QuitGame,
128 Next,
129 Previous,
130 Enter,
131}
132
133#[must_use]
138pub fn main_menu_event() -> MainMenuInput {
139 if let event::Event::Key(key) = event::read().expect("Error reading key event") {
141 match key.kind {
142 KeyEventKind::Press => {
144 flush_input_buffer();
145 if START_KEYS.contains(&key.code) {
147 MainMenuInput::Start
148 } else if QUIT_KEYS.contains(&key.code) {
150 MainMenuInput::QuitGame
151 } else if PARAMETERS_KEYS.contains(&key.code) {
152 MainMenuInput::Parameters
153 } else if FRUITS_KEYS.contains(&key.code) {
154 MainMenuInput::Fruits
155 } else if VELOCITY_KEYS.contains(&key.code) {
156 MainMenuInput::Speed
157 } else if HELP_KEYS.contains(&key.code) {
158 MainMenuInput::Doc
159 } else if HIGH_SCORE_KEYS.contains(&key.code) {
160 MainMenuInput::Highs
161 } else if NEXT_KEYS.contains(&key.code) {
162 MainMenuInput::Next
163 } else if PREVIOUS_KEYS.contains(&key.code) {
164 MainMenuInput::Previous
165 } else if ENTER_KEYS.contains(&key.code) {
166 MainMenuInput::Enter
167 } else {
168 MainMenuInput::Main
169 }
170 }
171 _ => MainMenuInput::Main,
172 }
173 } else {
174 MainMenuInput::Main
175 }
176}
177fn flush_input_buffer() {
178 while event::poll(std::time::Duration::from_secs(0)).unwrap_or(false) {
179 let _ = crossterm::event::read(); }
181}