use crate::controls::direction::Direction;
use crate::controls::speed::Speed;
use crate::game_logic::fruits_manager::FruitsManager;
use crate::game_logic::high_score::{HighScore, HighScoreManager};
use crate::game_logic::state::{GameOverMenu, GameState, GameStatus};
use crate::graphics::sprites::fruit::Fruit;
use crate::graphics::sprites::map::Map;
use crate::graphics::sprites::snake_body::SnakeBody;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::{Duration, Instant};
use tracing::{debug, error, info, trace, trace_span, warn};
pub fn playing_logic_loop(
direction: &Arc<RwLock<Direction>>,
snake: &Arc<RwLock<SnakeBody>>,
gs: &Arc<RwLock<GameState>>,
carte: &Arc<RwLock<Map>>,
fruits_manager: &Arc<RwLock<FruitsManager>>,
(game_speed, snake_symbols, negative_size_fruits): (Speed, String, bool),
) {
let mut gsc;
debug!(fruits = ?fruits_manager.read().unwrap().get_fruits(), "Initial fruits on map");
loop {
let _span = trace_span!("logic_iteration").entered();
let loop_start = Instant::now();
gsc = gs.read().unwrap().status.clone();
match gsc {
GameStatus::Playing => {
let mut write_guard = snake.write().unwrap();
let position = write_guard.ramp(&direction.read().unwrap(), &carte.read().unwrap());
debug!(?position, "Snake moved");
let fruits = fruits_manager.read().unwrap().eat_some_fruits(position);
if let Some(fruits) = fruits {
debug!(count = fruits.len(), "Fruits eaten by snake");
let score_fruits = fruits.iter().map(Fruit::get_score).sum::<i32>();
let size_effect = fruits.iter().map(Fruit::get_grow_snake).sum::<i16>();
info!(?fruits, "Fruit characteristics");
if negative_size_fruits || size_effect > 0 {
write_guard.relative_size_change(size_effect);
}
let fruit_impact = score_fruits * i32::from(game_speed.score_modifier());
info!(
fruit_impact = fruit_impact,
"Fruit impact on score because of speed score modifier"
);
let mut state_guard_gs = gs.write().unwrap();
if fruit_impact < 0 {
state_guard_gs.score = state_guard_gs
.score
.saturating_sub(fruit_impact.unsigned_abs());
} else {
state_guard_gs.score = state_guard_gs
.score
.saturating_add(fruit_impact.unsigned_abs());
}
info!(
score = state_guard_gs.score,
snake_size = write_guard.body.len(),
"Current game metrics"
);
fruits_manager.write().unwrap().replace_fruits(&fruits);
debug!(fruits = ?fruits_manager.read().unwrap().get_fruits(), "Current fruits on map");
}
if write_guard.is_snake_eating_itself() {
let mut state_guard = gs.write().unwrap();
if (state_guard.life) >= 1 {
state_guard.life -= 1;
warn!(
remaining_life = state_guard.life,
"Ouch! Did that taste good? You bit yourself!",
);
}
if state_guard.life == 0 {
error!(
score = state_guard.score,
"FATAL: Snake succumbed to its injuries. Game Over!"
);
state_guard.status = GameStatus::GameOver(GameOverMenu::default());
if let Ok(manager) = HighScoreManager::new() {
let score_value: u32 = state_guard.score;
let rank = manager.save_score(&HighScore::new(
snake_symbols.clone(),
score_value,
game_speed.to_string(),
));
state_guard.rank = rank.unwrap_or(None);
}
}
}
}
GameStatus::Restarting => {
sleep(Duration::from_secs(1));
gs.write().unwrap().reset();
snake.write().unwrap().reset();
*direction.write().unwrap() = Direction::Right;
fruits_manager.write().unwrap().reset();
}
GameStatus::ByeBye | GameStatus::Menu => break,
GameStatus::Paused | GameStatus::GameOver(_) => {}
}
let elapsed = loop_start.elapsed();
trace!(?elapsed, "Logic loop iteration finished");
sleep(Duration::from_millis(game_speed.ms_value()));
}
}