use crate::controls::direction::Direction;
use crate::graphics::graphic_block::{GraphicBlock, Position};
use crate::graphics::sprites::map::Map;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::prelude::{Style, Widget};
use ratatui::style::{Color, Modifier};
use ratatui::widgets::WidgetRef;
#[derive(Clone)]
pub struct SnakeBody<'a> {
pub(crate) body: Vec<GraphicBlock<'a>>,
case_size: u16,
position_ini: Position,
size_ini: u16,
}
impl<'a> SnakeBody<'a> {
#[must_use]
pub fn new(
body_image: &'a str,
head_image: &'a str,
nb: u16,
position: Position,
case_size: u16,
) -> SnakeBody<'a> {
let snake_style = Style {
fg: Some(Color::White),
bg: Some(Color::Reset), underline_color: Some(Color::White),
add_modifier: Modifier::ITALIC,
sub_modifier: Modifier::BOLD,
};
let Position { x, y } = position;
let mut body = Vec::with_capacity(nb as usize);
body.push(GraphicBlock::new(
Position { x, y },
head_image,
snake_style,
));
for i in 1..nb {
body.push(GraphicBlock::new(
Position {
x: x.saturating_sub(case_size * i),
y,
},
body_image,
snake_style,
));
}
SnakeBody {
body,
case_size,
position_ini: position,
size_ini: nb,
}
}
pub fn reset(&mut self) {
self.body.truncate(self.size_ini as usize);
self.body[0].set_position(self.position_ini.clone());
for i in 1..self.size_ini {
self.body[i as usize].set_position(Position {
x: self.position_ini.x.saturating_sub(self.case_size * i),
y: self.position_ini.y,
});
}
}
pub fn ramping_body(&mut self, previous_head: &Position) {
let mut current = previous_head.clone();
let mut previous = current;
let has_grown_by_one = false;
for i in 1..self.body.len() {
current = self.body[i].get_position().clone();
self.body[i].set_position(previous);
previous = current;
if !self.body[i].enabled && !has_grown_by_one {
self.body[i].enable();
}
}
}
#[must_use]
pub fn is_snake_eating_itself(&self) -> bool {
let head = self.body[0].get_position();
for b in self.body.iter().skip(1) {
if head == b.get_position() {
return true;
}
}
false
}
pub fn left(&mut self) {
let current = &self.body[0].get_position().clone();
self.body[0].position.x -= self.case_size;
self.ramping_body(current);
}
pub fn right(&mut self) {
let current = &self.body[0].get_position().clone();
self.body[0].position.x += self.case_size;
self.ramping_body(current);
}
pub fn up(&mut self) {
let current = &self.body[0].get_position().clone();
self.body[0].position.y -= 1;
self.ramping_body(current);
}
pub fn down(&mut self) {
let current = &self.body[0].get_position().clone();
self.body[0].position.y += 1;
self.ramping_body(current);
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn ramp(&mut self, direction: &Direction, carte: &Map) -> &Position {
match direction {
Direction::Up => self.up(),
Direction::Down => self.down(),
Direction::Left => self.left(),
Direction::Right => self.right(),
}
if carte.out_of_map(self.body[0].get_position()) {
let new_position = carte.out_of_map_reverse_position(self.body[0].get_position());
self.body[0].set_position(new_position);
}
self.body[0].get_position()
}
fn _get_widget(&self) -> impl Widget + 'a {
self.clone()
}
pub fn relative_size_change(&mut self, nb: i16) {
if nb > 0 {
for _ in 0..nb {
let mut block_to_add = self
.body
.last()
.expect("Snake body has no elements ! Something went wrong")
.clone();
block_to_add.disable();
self.body.push(block_to_add);
}
} else {
#[allow(clippy::cast_sign_loss)]
let sub = self.body.len().saturating_sub((-nb) as usize);
let to_keep = if sub < self.size_ini as usize {
self.size_ini as usize
} else {
sub
};
self.body.truncate(to_keep);
}
}
}
impl Widget for SnakeBody<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
self.render_ref(area, buf);
}
}
impl Widget for &SnakeBody<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
self.render_ref(area, buf);
}
}
impl WidgetRef for SnakeBody<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
for body_element in &self.body {
body_element.render_ref(area, buf);
}
}
}