rsnaker/graphics/menus/
messages.rs

1use crate::game_logic::state::GameOverMenu;
2use crate::graphics::menus::main_menu::Button;
3use crate::graphics::menus::utils_layout::{
4    frame_vertically_centered_rect, render_full_centered_paragraph,
5};
6use ratatui::style::Color;
7use ratatui::text::{Line, Span, Text};
8use ratatui::widgets::Paragraph;
9use ratatui::Frame;
10
11// For ASCII art use AI or a generator as:
12// http://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=Pause
13// For text display: https://ratatui.rs/recipes/render/display-text/
14//Or (but use a dependency): https://crates.io/crates/tui-big-text
15pub const SNAKE_LOGO: &str = "\
16███████╗ ███╗   ██╗  █████╗  ██╗  ██╗ ███████╗
17██╔════╝ ████╗  ██║ ██╔══██╗ ██║ ██╔╝ ██╔════╝
18███████╗ ██╔██╗ ██║ ███████║ █████╔╝  █████╗  
19╚════██║ ██║╚██╗██║ ██╔══██║ ██╔═██╗  ██╔══╝  
20███████║ ██║ ╚████║ ██║  ██║ ██║  ██╗ ███████╗
21╚══════╝ ╚═╝  ╚═══╝ ╚═╝  ╚═╝ ╚═╝  ╚═╝ ╚══════╝";
22
23// Define the control table as a constant
24pub const CONTROLS_TABLE: &str = "\
25+--------+------+------+-------+-----+-------+
26|Controls| ←↕→  |  Q   | P / ⎵ |  M  |   R   |
27+--------+------+------+-------+-----+-------+
28|Effects | Move | Quit | Pause | Menu| Start |
29+--------+------+------+-------+-----+-------+";
30const GAME_OVER_TEXT: &str = "\n\
31 ██████╗  █████╗ ███╗   ███╗███████╗       ██████╗ ██╗   ██╗███████╗██████╗ \n\
32██╔═══   ██╔══██╗████╗ ████║██╔════╝      ██╔═══██╗██║   ██║██╔════╝██╔══██╗\n\
33██║ ████║███████║██╔████╔██║█████╗        ██║   ██║██║   ██║█████╗  ██████╔╝\n\
34██║   ██║██╔══██║██║╚██╔╝██║██╔══╝        ██║   ██║██║   ██║██╔══╝  ██╔══██╗\n\
35╚██████╔╝██║  ██║██║ ╚═╝ ██║███████╗      ╚██████╔╝╚██████╔╝███████╗██║  ██║\n\
36 ╚═════╝ ╚═╝  ╚═╝╚═╝     ╚═╝╚══════╝       ╚═════╝  ╚═════╝ ╚══════╝╚═╝  ╚═╝";
37
38const PAUSE_TEXT: &str = "\n\
39██████╗  █████╗ ██╗   ██╗███████╗███████╗\n\
40██╔══██╗██╔══██╗██║   ██║██╔════╝██╔════╝\n\
41██████╔╝███████║██║   ██║███████╗█████╗  \n\
42██╔═══╝ ██╔══██║██║   ██║╚════██║██╔══╝  \n\
43██║     ██║  ██║╚██████╔╝███████║███████╗\n\
44╚═╝     ╚═╝  ╚═╝ ╚═════╝ ╚══════╝╚══════╝";
45//goodbye
46const MENU_TEXT: &str = "\n\
47███╗   ███╗███████╗███╗   ██╗██╗   ██╗
48████╗ ████║██╔════╝████╗  ██║██║   ██║
49██╔████╔██║█████╗  ██╔██╗ ██║██║   ██║
50██║╚██╔╝██║██╔══╝  ██║╚██╗██║██║   ██║
51██║ ╚═╝ ██║███████╗██║ ╚████║╚██████╔╝
52╚═╝     ╚═╝╚══════╝╚═╝  ╚═══╝ ╚═════╝ ";
53
54const FAREWELL_TEXT: &str = "\n\
55██████╗ ██╗   ██╗███████╗    ██████╗ ██╗   ██╗███████╗
56██╔══██╗╚██╗ ██╔╝██╔════╝    ██╔══██╗╚██╗ ██╔╝██╔════╝
57██████╔╝ ╚████╔╝ █████╗█████╗██████╔╝ ╚████╔╝ █████╗  
58██╔══██╗  ╚██╔╝  ██╔══╝╚════╝██╔══██╗  ╚██╔╝  ██╔══╝  
59██████╔╝   ██║   ███████╗    ██████╔╝   ██║   ███████╗
60╚═════╝    ╚═╝   ╚══════╝    ╚═════╝    ╚═╝   ╚══════╝";
61
62const RESTART_TEXT: &str = "\n\
63██████╗ ███████╗███████╗████████╗ █████╗ ██████╗ ████████╗
64██╔══██╗██╔════╝██╔════╝╚══██╔══╝██╔══██╗██╔══██╗╚══██╔══╝
65██████╔╝█████╗  ███████╗   ██║   ███████║██████╔╝   ██║   
66██╔══██╗██╔══╝  ╚════██║   ██║   ██╔══██║██╔══██╗   ██║   
67██║  ██║███████╗███████║   ██║   ██║  ██║██║  ██║   ██║   
68╚═╝  ╚═╝╚══════╝╚══════╝   ╚═╝   ╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝   ";
69
70pub fn pause_paragraph(frame: &mut Frame) {
71    render_full_centered_paragraph(frame, PAUSE_TEXT, None);
72}
73pub fn byebye_paragraph(frame: &mut Frame) {
74    render_full_centered_paragraph(frame, FAREWELL_TEXT, Some(Color::DarkGray));
75}
76pub fn restart_paragraph(frame: &mut Frame) {
77    render_full_centered_paragraph(frame, RESTART_TEXT, Some(Color::LightYellow));
78}
79pub fn menu_paragraph(frame: &mut Frame) {
80    //Rgb Purple badly managed by old terminals Color::Rgb(128, 0, 128)
81    render_full_centered_paragraph(frame, MENU_TEXT, Some(Color::Gray));
82}
83pub fn game_over_paragraph(
84    frame: &mut Frame,
85    selection: &GameOverMenu,
86    score: u32,
87    rank: Option<usize>,
88) {
89    let mut lines = vec![];
90    //Get the GAME OVER text in red
91    for logo_line in GAME_OVER_TEXT.lines() {
92        lines.push(Line::from(logo_line).style(ratatui::style::Style::default().fg(Color::Red)));
93    }
94
95    //Display the score text
96    lines.push(Line::default());
97    lines.push(Line::from(vec![
98        Span::raw("Score: "),
99        Span::styled(
100            format!("{score}"),
101            ratatui::style::Style::default().fg(Color::Yellow),
102        ),
103    ]));
104    // if among top 10, display the ranking
105    if let Some(r) = rank {
106        lines.push(Line::from(vec![
107            Span::raw("Ranking: "),
108            Span::styled(
109                format!("#{r}"),
110                ratatui::style::Style::default().fg(Color::Green),
111            ),
112            Span::raw(" among top 10!"),
113        ]));
114    }
115    lines.push(Line::default());
116
117    // Display the option buttons to restart/Menu/Quit for the player
118    let mut restart_button = Button::new("Restart 🎮");
119    let mut menu_button = Button::new("Menu 🗺");
120    let mut quit_button = Button::new("Quit ");
121
122    restart_button.selected(selection == &GameOverMenu::Restart);
123    menu_button.selected(selection == &GameOverMenu::Menu);
124    quit_button.selected(selection == &GameOverMenu::Quit);
125
126    let mut combined_spans: Vec<Span> = restart_button.to_spans();
127    combined_spans.extend(menu_button.to_spans());
128    combined_spans.extend(quit_button.to_spans());
129    lines.push(Line::from(combined_spans));
130
131    // Render all the previous elements
132    let nb_lines = lines.len();
133    frame.render_widget(
134        Paragraph::new(Text::from(lines)).centered(),
135        frame_vertically_centered_rect(frame.area(), nb_lines),
136    );
137    //Render the frame all around the edges
138    //NB: no red everywhere on block as with "//.style(ratatui::style::Style::default().fg(Color::Red)),"
139    //Because otherwise the button's color of the game over screen will be lost
140    frame.render_widget(ratatui::widgets::Block::default(), frame.area());
141}