use std::collections::HashSet; use bevy::{color::palettes::tailwind::*, prelude::*}; use rand::RngExt; use crate::{domain::*, tools::split_by_grid}; /// Общая механика движения fn moving( velocity: &f32, time: &Time, direction: &mut Direction, transform: &mut Transform, entity: Entity, commands: &mut Commands, switch_direction: impl FnOnce(Position) -> Option, ) { // первая часть движения - до границы сетки let delta = velocity * time.delta_secs(); let src = direction.get_coord(transform); let sign = direction.get_change_sign(); let dst = src + sign * delta; let mut splited = split_by_grid(src, dst, GRID_SIZE as f32).into_iter(); let first = splited.next().unwrap_or((0.0, 0.0)); direction.set_coord(transform, first.0); // вторая часть движения - после границы сетки if let Some(second) = splited.next() { let delta = second.1.abs(); let pos = Position::from(&*transform); if let Some(dir) = switch_direction(pos) { trace!("set new direction for {entity} in {pos} --> {dir}"); *direction = dir; } let src = direction.get_coord(&*transform); let sign = direction.get_change_sign(); let dst = src + sign * delta; direction.set_coord(transform, dst); // создать событие прохождения границы сетки commands.entity(entity).trigger(|e| GridBarier::new(e, pos, *direction)); } } /// Система движения головы змеи pub fn head_moving( query: Query<(&mut Transform, &mut Direction, &Velocity, &Head, Entity)>, time: Res>, mut snake_path: ResMut, mut commands: Commands, ) { let _span = trace_span!("head_moving").entered(); for (mut transform, mut direction, Velocity(velocity), head, entity) in query.into_iter() { let next_dir = head.0; let dir = *direction; let snake_path = &mut snake_path; let switch_direction = move |pos: Position| { if next_dir != dir { snake_path.insert(pos, next_dir); Some(next_dir) } else { None } }; moving(velocity, &time, &mut direction, &mut transform, entity, &mut commands, switch_direction); } } #[allow(clippy::type_complexity)] /// Система движения тела змеи pub fn snake_moving( query: Query<(&mut Transform, &mut Direction, &Velocity, Entity), (With, Without)>, time: Res>, snake_path: Res, mut commands: Commands, ) { let _span = trace_span!("snake_moving").entered(); for (mut transform, mut direction, Velocity(velocity), entity) in query.into_iter() { let snake_path = &snake_path; let switch_direction = move |pos: Position| snake_path.get(&pos).cloned(); moving(velocity, &time, &mut direction, &mut transform, entity, &mut commands, switch_direction); } } /// Система спавна еды при необходимости pub fn spawn_meal_if_needed( time: Res>, // mut meal_timer: ResMut, query: Query<&Transform, With>, mut commands: Commands, ) { if meal_timer.tick(time.elapsed()).just_finished() && query.count() < MAX_MEALS_ON_FIELD as usize { let used_positions = query.iter().map(Position::from).collect::>(); let mut rng = rand::rng(); let pos = loop { const ABS: i8 = (FIELD_SIZE as f32 / 2.0) as i8; let rand_x = rng.random::() % ABS; let rand_y = rng.random::() % ABS; let rand_pos = Position::new(rand_x, rand_y); if !used_positions.contains(&rand_pos) { break rand_pos; } }; commands.spawn(( Meal, Sprite::from_color(YELLOW_400, vec2(MEAL_SIZE as f32, MEAL_SIZE as f32)), // Transform { translation: pos.translation().extend(0.0), ..Default::default() }, )); trace!("new meal spawned at {pos}"); } }