use crate::game_logic::fruits_manager::FruitsManager;
use crate::game_logic::state::{GameState, GameStatus};
use crate::graphics::menus;
use crate::graphics::sprites::map::Map;
use crate::graphics::sprites::snake_body::SnakeBody;
use ratatui::layout::Rect;
use ratatui::widgets::Paragraph;
use ratatui::{DefaultTerminal, Frame};
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::{Duration, Instant};
const BOTTOM_SPEED_FPS_SCORE_RECT: Rect = Rect::new(1, 9999, 70, 1);
const LIFE_RECT: Rect = Rect::new(1, 0, 60, 1);
const NB_OF_FRAMES_WINDOW: f64 = 1_000.0;
const TOO_MUCH_LIVES_TO_DISPLAY: &str = " life: ❤️❤️❤️❤️❤️... ";
pub fn playing_render_loop<'a: 'b, 'b>(
carte: &Arc<RwLock<Map>>,
fruits_manager: &Arc<RwLock<FruitsManager<'a, 'b>>>,
state: &Arc<RwLock<GameState>>,
serpent: &Arc<RwLock<SnakeBody>>,
caps_fps: bool,
speed_effect: (u16, &str),
terminal: &mut DefaultTerminal,
) {
let speed_text = format!("Speed: x{}{}", speed_effect.0, speed_effect.1);
let mut rendering_break = false;
let mut need_carte_resize = false;
let mut frame_count = 0f64;
let mut start_windows_time = Instant::now();
let mut start_frame_time: Instant;
let target_frame_time = Duration::from_secs_f64(1.0 / 60.0);
'render_loop: loop {
start_frame_time = Instant::now();
frame_count += 1.0;
if frame_count >= NB_OF_FRAMES_WINDOW {
frame_count = 1.0;
start_windows_time = Instant::now();
}
terminal
.draw(|frame| {
let area = frame.area();
{
let map_guard = carte.read().unwrap();
let area_map = map_guard.area();
frame.render_widget(map_guard.get_widget(), *area_map);
if area.height != area_map.height || area.width != area_map.width {
need_carte_resize = true;
}
}
if need_carte_resize {
carte.write().unwrap().resize_to_terminal(area);
fruits_manager.write().unwrap().reset_to_terminal_size();
need_carte_resize = false;
}
{
frame.render_widget(
Paragraph::new(format!(
"{speed_text} | FPS: {} | Score: {} ",
(frame_count / start_windows_time.elapsed().as_secs_f64()).floor(),
state.read().unwrap().score
)),
BOTTOM_SPEED_FPS_SCORE_RECT.clamp(frame.area()),
);
}
{
let state_guard = state.read().unwrap();
let life = state_guard.life as usize;
frame.render_widget(
Paragraph::new(if life > 5 {
TOO_MUCH_LIVES_TO_DISPLAY.to_string()
} else {
format!(" life: {} ", "❤️ ".repeat(life))
}),
LIFE_RECT.clamp(frame.area()),
);
}
{
let snake_read = serpent.read().unwrap(); frame.render_widget(&*snake_read, frame.area());
}
{
let fruits_manager_read = fruits_manager.read().unwrap(); frame.render_widget(&*fruits_manager_read, frame.area());
}
let state_guard = state.read().unwrap();
rendering_break = game_state_render(
&state_guard.status,
state_guard.score,
state_guard.rank,
frame,
);
})
.expect("bad rendering, check sprites position");
if rendering_break {
sleep(Duration::from_millis(1000));
break 'render_loop;
}
let frame_time = start_frame_time.elapsed();
if caps_fps && frame_time < target_frame_time {
sleep(target_frame_time.saturating_sub(frame_time));
}
}
}
fn game_state_render(
state: &GameStatus,
score: u32,
rank: Option<usize>,
frame: &mut Frame,
) -> bool {
let mut rendering_break = false;
match state {
GameStatus::Paused => {
menus::messages::pause_paragraph(frame);
}
GameStatus::GameOver(selection) => {
menus::messages::game_over_paragraph(frame, selection, score, rank);
}
GameStatus::ByeBye => {
menus::messages::byebye_paragraph(frame);
rendering_break = true;
}
GameStatus::Playing => (),
GameStatus::Restarting => {
menus::messages::restart_paragraph(frame);
}
GameStatus::Menu => {
menus::messages::menu_paragraph(frame);
rendering_break = true;
}
}
rendering_break
}