|  |  | @ -1,18 +1,10 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | #![feature(try_blocks)] |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | // Bevy basically forces "complex types" with Querys
 |  |  |  | // Bevy basically forces "complex types" with Querys
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #![allow(clippy::type_complexity)] |  |  |  | #![allow(clippy::type_complexity)] | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | use games::*; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | use itertools::Itertools; |  |  |  | use itertools::Itertools; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | use games::*; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #[cfg(test)] |  |  |  | // TODO: When line is "full" (has 10 children) clear line and add to score
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | mod test; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | // TODO: Space key: skip to end
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | // TODO: When piece is near wall and rotates, move it over if it fits
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | // TODO: Make falling based on a timer resource ticking
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | //       This allows us to tune the falling rate over time
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | // TODO: Preview next batch of pieces that will drop
 |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn main() { |  |  |  | fn main() { | 
			
		
	
		
		
			
				
					
					|  |  |  |     App::new() |  |  |  |     App::new() | 
			
		
	
	
		
		
			
				
					|  |  | @ -22,59 +14,35 @@ fn main() { | 
			
		
	
		
		
			
				
					
					|  |  |  |             game_type: GameType::Two, |  |  |  |             game_type: GameType::Two, | 
			
		
	
		
		
			
				
					
					|  |  |  |             ..default() |  |  |  |             ..default() | 
			
		
	
		
		
			
				
					
					|  |  |  |         }) |  |  |  |         }) | 
			
		
	
		
		
			
				
					
					|  |  |  |         .init_state::<GameState>() |  |  |  |         .init_state::<Falling>() | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         .init_resource::<ShapesBuffer>() |  |  |  |         .add_systems(Startup, (init_world, init_debug_ui)) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         .init_resource::<Score>() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .add_systems(Startup, (init_world, init_debug_ui, init_ui)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         // Input and basic systems
 |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         .add_systems( |  |  |  |         .add_systems( | 
			
		
	
		
		
			
				
					
					|  |  |  |             Update, |  |  |  |             Update, | 
			
		
	
		
		
			
				
					
					|  |  |  |             ( |  |  |  |             ( | 
			
		
	
		
		
			
				
					
					|  |  |  |                 kb_input.run_if(on_event::<KeyboardInput>), |  |  |  |                 kb_input.run_if(on_event::<KeyboardInput>), | 
			
		
	
		
		
			
				
					
					|  |  |  |                 toggle_state_visibility::<GameState>.run_if(state_changed::<GameState>), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             ), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .add_systems( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             Update, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             ( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 update_next_shapes |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     .run_if(resource_changed::<ShapesBuffer>.or(resource_added::<ShapesBuffer>)), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 add_piece |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     .run_if(not(any_with_component::<Shape>)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     .after(update_next_shapes), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 update_shape_blocks |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     .run_if(any_component_added::<Shape>.or(any_component_changed::<Shape>)), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 falling |  |  |  |                 falling | 
			
		
	
		
		
			
				
					
					|  |  |  |                     .run_if(in_state(GameState::Falling)) |  |  |  |                     .run_if(in_state(Falling::On)) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                     .run_if(clock_cycle(1.0)), |  |  |  |                     .run_if(clock_cycle(1.0)), | 
			
		
	
		
		
			
				
					
					|  |  |  |                 update_position.run_if(any_component_changed::<GridPosition>), |  |  |  |                 update_shape_blocks | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 deactivate_shape.run_if(any_component_removed::<Shape>), |  |  |  |                     .run_if(any_component_added::<Shape>.or(any_component_changed::<Shape>)), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 check_line_removal, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 // Clearing lines systems
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 clear_line.run_if(any_component_changed::<LineBlocks>), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 adjust_block_lines |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     .run_if(any_component_changed::<Line>) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     .after(clear_line), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             ), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         // UI systems
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .add_systems( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             Update, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             ( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 sync_resource_to_ui::<ShapesBuffer>.run_if(resource_changed::<ShapesBuffer>), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 sync_resource_to_ui::<Score>.run_if(resource_changed::<Score>), |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                 sync_singleton_to_ui::<Shape>.run_if(any_component_changed::<Shape>), |  |  |  |                 sync_singleton_to_ui::<Shape>.run_if(any_component_changed::<Shape>), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 sync_singleton_to_ui::<Orientation>.run_if(any_component_changed::<Orientation>), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 update_position, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 add_piece.run_if(not(any_with_component::<Shape>)), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 clear_line.run_if(any_component_changed::<LineBlocks>), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 adjust_block_lines.run_if(any_component_changed::<Line>), | 
			
		
	
		
		
			
				
					
					|  |  |  |             ), |  |  |  |             ), | 
			
		
	
		
		
			
				
					
					|  |  |  |         ) |  |  |  |         ) | 
			
		
	
		
		
			
				
					
					|  |  |  |         .add_systems(Update, draw_grid) |  |  |  |         .add_systems(Update, draw_grid) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         .add_observer(deactive_shape) | 
			
		
	
		
		
			
				
					
					|  |  |  |         .run(); |  |  |  |         .run(); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | const SCALE: f32 = 30.0; |  |  |  | const SCALE: f32 = 30.0; | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | // Declare the size of the play area
 |  |  |  | // Declare the size of the play area
 | 
			
		
	
		
		
			
				
					
					|  |  |  | const X_MAX: usize = 10; |  |  |  | const X_MAX: u32 = 10; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | const Y_MAX: usize = 20; |  |  |  | const Y_MAX: u32 = 20; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | // The blocks making up this shape
 |  |  |  | // The blocks making up this shape
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #[derive(Component)] |  |  |  | #[derive(Component)] | 
			
		
	
	
		
		
			
				
					|  |  | @ -114,8 +82,8 @@ struct Block; | 
			
		
	
		
		
			
				
					
					|  |  |  | #[derive(Component, Debug, Clone, Copy, PartialEq)] |  |  |  | #[derive(Component, Debug, Clone, Copy, PartialEq)] | 
			
		
	
		
		
			
				
					
					|  |  |  | #[require(Transform, Visibility)] |  |  |  | #[require(Transform, Visibility)] | 
			
		
	
		
		
			
				
					
					|  |  |  | struct GridPosition { |  |  |  | struct GridPosition { | 
			
		
	
		
		
			
				
					
					|  |  |  |     x: usize, |  |  |  |     x: u32, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     y: usize, |  |  |  |     y: u32, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | impl GridPosition { |  |  |  | impl GridPosition { | 
			
		
	
	
		
		
			
				
					|  |  | @ -131,8 +99,8 @@ impl GridPosition { | 
			
		
	
		
		
			
				
					
					|  |  |  |             Err(GameError::OutOfBoundsDown) |  |  |  |             Err(GameError::OutOfBoundsDown) | 
			
		
	
		
		
			
				
					
					|  |  |  |         } else { |  |  |  |         } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |             Ok(GridPosition { |  |  |  |             Ok(GridPosition { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 x: x as usize, |  |  |  |                 x: x as u32, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 y: y as usize, |  |  |  |                 y: y as u32, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             }) |  |  |  |             }) | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
	
		
		
			
				
					|  |  | @ -176,8 +144,8 @@ impl From<&GridPosition> for Vec3 { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | impl From<(usize, usize)> for GridPosition { |  |  |  | impl From<(u32, u32)> for GridPosition { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     fn from((x, y): (usize, usize)) -> GridPosition { |  |  |  |     fn from((x, y): (u32, u32)) -> GridPosition { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         GridPosition { x, y } |  |  |  |         GridPosition { x, y } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | @ -199,43 +167,57 @@ impl std::ops::AddAssign<&GridPosition> for GridPosition { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default, Component)] |  |  |  | #[derive(Component, Default, Event, Clone, Debug)] | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | enum GameState { |  |  |  | enum Orientation { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     #[default] |  |  |  |     #[default] | 
			
		
	
		
		
			
				
					
					|  |  |  |     Falling, |  |  |  |     Up, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     Pause, |  |  |  |     Left, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     Down, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     Right, | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #[derive(Resource, Debug)] |  |  |  | impl Orientation { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | struct Visuals { |  |  |  |     fn next(&self) -> Self { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     material: Handle<ColorMaterial>, |  |  |  |         match self { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     mesh: Handle<Mesh>, |  |  |  |             Self::Up => Self::Left, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Self::Left => Self::Down, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Self::Down => Self::Right, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Self::Right => Self::Up, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | #[derive(Resource, Debug, Default)] |  |  |  |     fn prev(&self) -> Self { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | struct Score(usize); |  |  |  |         match self { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |             Self::Up => Self::Right, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | impl Display for Score { |  |  |  |             Self::Right => Self::Down, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |  |  |  |             Self::Down => Self::Left, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         write!(f, "{}", self.0) |  |  |  |             Self::Left => Self::Up, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | /// ShapesBuffer resource stores non-active shapes
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | #[derive(Resource, Debug, Default)] |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | struct ShapesBuffer { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     /// Next stores a vector of 2N shapes that will come up in play
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     next: VecDeque<Shape>, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | impl Display for ShapesBuffer { |  |  |  | impl Display for Orientation { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |  |  |  |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
			
		
	
		
		
			
				
					
					|  |  |  |         if let Some(shape) = self.next.front() { |  |  |  |         match self { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             write!(f, "{shape}") |  |  |  |             Orientation::Up => write!(f, "up"), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         } else { |  |  |  |             Orientation::Down => write!(f, "down"), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             write!(f, "ERR") |  |  |  |             Orientation::Left => write!(f, "<-"), | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Orientation::Right => write!(f, "->"), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | #[derive(States, Clone, Eq, PartialEq, Debug, Hash, Default, Component)] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | enum Falling { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     #[default] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     On, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     Off, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | #[derive(Resource, Debug)] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | struct Visuals { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     material: Handle<ColorMaterial>, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     mesh: Handle<Mesh>, | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn init_world( |  |  |  | fn init_world( | 
			
		
	
	
		
		
			
				
					|  |  | @ -251,60 +233,11 @@ fn init_world( | 
			
		
	
		
		
			
				
					
					|  |  |  |         mesh: meshes.add(Rectangle::new(SCALE, SCALE)), |  |  |  |         mesh: meshes.add(Rectangle::new(SCALE, SCALE)), | 
			
		
	
		
		
			
				
					
					|  |  |  |     }); |  |  |  |     }); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     (0..Y_MAX).for_each(|i| { |  |  |  |     (0..20).for_each(|i| { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         info!("Spawning line {i}"); |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         commands.spawn((Line(i), LineBlocks::default())); |  |  |  |         commands.spawn((Line(i), LineBlocks::default())); | 
			
		
	
		
		
			
				
					
					|  |  |  |     }); |  |  |  |     }); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn init_ui(mut commands: Commands) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     commands |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .spawn(( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             Node { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 align_self: AlignSelf::Center, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 justify_self: JustifySelf::End, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 flex_direction: FlexDirection::Column, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 ..default() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             }, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             BackgroundColor(BLACK.into()), |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         )) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .with_children(|parent| { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             parent |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 .spawn((Node { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     flex_direction: FlexDirection::Column, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     align_items: AlignItems::Center, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     ..default() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 },)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 .with_children(|parent| { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     parent.spawn(Text::new("Next:")); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     parent.spawn((Text::new("???"), SyncResource::<ShapesBuffer>::default())); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 }); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             parent |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 .spawn((Node { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     flex_direction: FlexDirection::Column, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     align_items: AlignItems::Center, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     ..default() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 },)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 .with_children(|parent| { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     parent.spawn(Text::new("Score:")); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     parent.spawn((Text::new("???"), SyncResource::<Score>::default())); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 }); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         }); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     commands |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .spawn(( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             Node { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 align_self: AlignSelf::Center, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 justify_self: JustifySelf::Center, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 ..default() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             }, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             GameState::Pause, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         )) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .with_children(|parent| { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             parent.spawn(Text::new("Paused")); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         }); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | fn init_debug_ui(mut commands: Commands) { |  |  |  | fn init_debug_ui(mut commands: Commands) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     commands |  |  |  |     commands | 
			
		
	
		
		
			
				
					
					|  |  |  |         .spawn(( |  |  |  |         .spawn(( | 
			
		
	
	
		
		
			
				
					|  |  | @ -320,6 +253,10 @@ fn init_debug_ui(mut commands: Commands) { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 Node::default(), |  |  |  |                 Node::default(), | 
			
		
	
		
		
			
				
					
					|  |  |  |                 children![ |  |  |  |                 children![ | 
			
		
	
		
		
			
				
					
					|  |  |  |                     (Text::new("SHAPE"), SyncSingleton::<Shape>::default()), |  |  |  |                     (Text::new("SHAPE"), SyncSingleton::<Shape>::default()), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     ( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         Text::new("ORIENTATION"), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         SyncSingleton::<Orientation>::default() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                     ), | 
			
		
	
		
		
			
				
					
					|  |  |  |                 ], |  |  |  |                 ], | 
			
		
	
		
		
			
				
					
					|  |  |  |             )); |  |  |  |             )); | 
			
		
	
		
		
			
				
					
					|  |  |  |         }); |  |  |  |         }); | 
			
		
	
	
		
		
			
				
					|  |  | @ -437,6 +374,10 @@ impl Shape { | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     fn rotate(&mut self) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         *self = self.rotated(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     fn coordinates( |  |  |  |     fn coordinates( | 
			
		
	
		
		
			
				
					
					|  |  |  |         &self, |  |  |  |         &self, | 
			
		
	
		
		
			
				
					
					|  |  |  |         center: &GridPosition, |  |  |  |         center: &GridPosition, | 
			
		
	
	
		
		
			
				
					|  |  | @ -508,13 +449,13 @@ fn update_position( | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | // TODO: Inline this to when movement occurs
 |  |  |  | // TODO: Inline this to when movement occurs
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn update_shape_blocks( |  |  |  | fn update_shape_blocks( | 
			
		
	
		
		
			
				
					
					|  |  |  |     query: Query<(Entity, &Shape, &GridPosition), Or<(Added<Shape>, Changed<Shape>)>>, |  |  |  |     query: Query<(Entity, &Shape, &Orientation, &GridPosition), Or<(Added<Shape>, Changed<Shape>)>>, | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     mut blocks: Query<&mut GridPosition, (With<ShapeBlock>, Without<Shape>)>, |  |  |  |     mut blocks: Query<&mut GridPosition, (With<ShapeBlock>, Without<Shape>)>, | 
			
		
	
		
		
			
				
					
					|  |  |  |     mut commands: Commands, |  |  |  |     mut commands: Commands, | 
			
		
	
		
		
			
				
					
					|  |  |  |     visuals: Res<Visuals>, |  |  |  |     visuals: Res<Visuals>, | 
			
		
	
		
		
			
				
					
					|  |  |  | ) { |  |  |  | ) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     query.iter().for_each(|(e, s, center)| { |  |  |  |     query.iter().for_each(|(e, s, o, center)| { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         debug!("Setting piece: {e:?} {center:?}\n{}", s.as_ascii()); |  |  |  |         info!("Setting piece: {e:?} {o:?} {center:?}\n{}", s.as_ascii()); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         if blocks.is_empty() { |  |  |  |         if blocks.is_empty() { | 
			
		
	
		
		
			
				
					
					|  |  |  |             let mesh = Mesh2d(visuals.mesh.clone()); |  |  |  |             let mesh = Mesh2d(visuals.mesh.clone()); | 
			
		
	
	
		
		
			
				
					|  |  | @ -539,9 +480,9 @@ fn update_shape_blocks( | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn kb_input( |  |  |  | fn kb_input( | 
			
		
	
		
		
			
				
					
					|  |  |  |     mut events: EventReader<KeyboardInput>, |  |  |  |     mut events: EventReader<KeyboardInput>, | 
			
		
	
		
		
			
				
					
					|  |  |  |     mut query: Query<(Entity, &mut Shape)>, |  |  |  |     mut query: Query<(Entity, &Orientation, &mut Shape)>, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     curr: Res<State<GameState>>, |  |  |  |     curr: Res<State<Falling>>, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     mut next: ResMut<NextState<GameState>>, |  |  |  |     mut next: ResMut<NextState<Falling>>, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     mut commands: Commands, |  |  |  |     mut commands: Commands, | 
			
		
	
		
		
			
				
					
					|  |  |  | ) { |  |  |  | ) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     events.read().for_each( |  |  |  |     events.read().for_each( | 
			
		
	
	
		
		
			
				
					|  |  | @ -549,7 +490,7 @@ fn kb_input( | 
			
		
	
		
		
			
				
					
					|  |  |  |              key_code, state, .. |  |  |  |              key_code, state, .. | 
			
		
	
		
		
			
				
					
					|  |  |  |          }| { |  |  |  |          }| { | 
			
		
	
		
		
			
				
					
					|  |  |  |             if let ButtonState::Pressed = state { |  |  |  |             if let ButtonState::Pressed = state { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 query.iter_mut().for_each(|(e, mut s)| { |  |  |  |                 query.iter_mut().for_each(|(e, o, mut s)| { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                     match key_code { |  |  |  |                     match key_code { | 
			
		
	
		
		
			
				
					
					|  |  |  |                         // Up arrow should rotate if in falling mode
 |  |  |  |                         // Up arrow should rotate if in falling mode
 | 
			
		
	
		
		
			
				
					
					|  |  |  |                         // Only move up if in falling::off mode
 |  |  |  |                         // Only move up if in falling::off mode
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -565,9 +506,9 @@ fn kb_input( | 
			
		
	
		
		
			
				
					
					|  |  |  |                         KeyCode::ArrowRight => { |  |  |  |                         KeyCode::ArrowRight => { | 
			
		
	
		
		
			
				
					
					|  |  |  |                             commands.entity(e).trigger(Movement::Right); |  |  |  |                             commands.entity(e).trigger(Movement::Right); | 
			
		
	
		
		
			
				
					
					|  |  |  |                         } |  |  |  |                         } | 
			
		
	
		
		
			
				
					
					|  |  |  |                         KeyCode::Escape => next.set(match curr.get() { |  |  |  |                         KeyCode::Space => next.set(match curr.get() { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             GameState::Falling => GameState::Pause, |  |  |  |                             Falling::On => Falling::Off, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             GameState::Pause => GameState::Falling, |  |  |  |                             Falling::Off => Falling::On, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                         }), |  |  |  |                         }), | 
			
		
	
		
		
			
				
					
					|  |  |  |                         KeyCode::Digit1 => *s = Shape::new_t(), |  |  |  |                         KeyCode::Digit1 => *s = Shape::new_t(), | 
			
		
	
		
		
			
				
					
					|  |  |  |                         KeyCode::Digit2 => *s = Shape::new_o(), |  |  |  |                         KeyCode::Digit2 => *s = Shape::new_o(), | 
			
		
	
	
		
		
			
				
					|  |  | @ -588,7 +529,7 @@ fn draw_grid(mut gizmos: Gizmos) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     gizmos |  |  |  |     gizmos | 
			
		
	
		
		
			
				
					
					|  |  |  |         .grid_2d( |  |  |  |         .grid_2d( | 
			
		
	
		
		
			
				
					
					|  |  |  |             Isometry2d::IDENTITY, |  |  |  |             Isometry2d::IDENTITY, | 
			
		
	
		
		
			
				
					
					|  |  |  |             UVec2::new(X_MAX as u32, Y_MAX as u32), |  |  |  |             UVec2::new(X_MAX, Y_MAX), | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             Vec2::new(SCALE, SCALE), |  |  |  |             Vec2::new(SCALE, SCALE), | 
			
		
	
		
		
			
				
					
					|  |  |  |             GREEN, |  |  |  |             GREEN, | 
			
		
	
		
		
			
				
					
					|  |  |  |         ) |  |  |  |         ) | 
			
		
	
	
		
		
			
				
					|  |  | @ -616,11 +557,13 @@ fn clock_cycle(n: f32) -> impl FnMut(Res<Time>, Local<f32>) -> bool { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn add_piece(mut commands: Commands, mut shapes: ResMut<ShapesBuffer>) { |  |  |  | fn add_piece(mut commands: Commands) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     // TODO: Choose a different piece
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     commands |  |  |  |     commands | 
			
		
	
		
		
			
				
					
					|  |  |  |         .spawn(( |  |  |  |         .spawn(( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Orientation::default(), | 
			
		
	
		
		
			
				
					
					|  |  |  |             GridPosition::default(), |  |  |  |             GridPosition::default(), | 
			
		
	
		
		
			
				
					
					|  |  |  |             shapes.next.pop_front().unwrap(), |  |  |  |             Shape::default(), | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         )) |  |  |  |         )) | 
			
		
	
		
		
			
				
					
					|  |  |  |         .observe(movement); |  |  |  |         .observe(movement); | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | @ -628,59 +571,37 @@ fn add_piece(mut commands: Commands, mut shapes: ResMut<ShapesBuffer>) { | 
			
		
	
		
		
			
				
					
					|  |  |  | /// When a line reaches 10 blocks, clear it
 |  |  |  | /// When a line reaches 10 blocks, clear it
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn clear_line( |  |  |  | fn clear_line( | 
			
		
	
		
		
			
				
					
					|  |  |  |     changed_lines: Query<Entity, Changed<LineBlocks>>, |  |  |  |     changed_lines: Query<Entity, Changed<LineBlocks>>, | 
			
		
	
		
		
			
				
					
					|  |  |  |     mut lines: Query<&mut Line>, |  |  |  |     mut lines: Query<(Entity, &LineBlocks, &mut Line)>, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     line_blocks: Query<&LineBlocks>, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     mut score: ResMut<Score>, |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     mut commands: Commands, |  |  |  |     mut commands: Commands, | 
			
		
	
		
		
			
				
					
					|  |  |  | ) { |  |  |  | ) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     let mut cleared_lines: Vec<usize> = changed_lines |  |  |  |     let cleared_lines: Vec<usize> = changed_lines | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         .iter() |  |  |  |         .iter() | 
			
		
	
		
		
			
				
					
					|  |  |  |         .filter_map(|e| try { (Some(e)?, line_blocks.get(e).ok()?, lines.get(e).ok()?) } ) |  |  |  |         .filter_map(|e| lines.get(e).ok()) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         .filter_map(|(e, lb, Line(i))| { |  |  |  |         .filter_map(|(e, lb, Line(i))| { | 
			
		
	
		
		
			
				
					
					|  |  |  |             if lb.0.len() == 10 { |  |  |  |             if lb.0.len() == 10 { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 commands.entity(e).despawn_related::<LineBlocks>().insert(LineBlocks::default()); |  |  |  |                 commands.entity(e).despawn_related::<LineBlocks>(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 score.0 += 1; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 info!("New score: {:?}", score.0); |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                 Some(*i) |  |  |  |                 Some(*i) | 
			
		
	
		
		
			
				
					
					|  |  |  |             } else { |  |  |  |             } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 None |  |  |  |                 None | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         }) |  |  |  |         }) | 
			
		
	
		
		
			
				
					
					|  |  |  |         .sorted() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         .collect(); |  |  |  |         .collect(); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if !cleared_lines.is_empty() { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     info!("Cleared lines: {:?}", cleared_lines); |  |  |  |     info!("Cleared lines: {:?}", cleared_lines); | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |         #[cfg(debug_assertions)] |  |  |  |     for (idx, cleared_line_number) in cleared_lines.into_iter().sorted().enumerate() { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         { |  |  |  |         info!("Processing line {cleared_line_number} ({idx})"); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             debug_assert_eq!(lines.iter().count(), 20, "There should be 20 lines"); |  |  |  |         let cleared_line_number = cleared_line_number - idx; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             // Check that all line numbers are present
 |  |  |  |         lines.iter_mut().for_each(|(_, _, mut l)| { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             lines.iter().map(|Line(i)| i).sorted().enumerate().for_each(|(i, line_num)| { |  |  |  |             let dest = if l.0 > cleared_line_number { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 debug_assert_eq!(i, *line_num, "Line numbers should match their sorted index"); |  |  |  |                 l.0 - 1 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             }); |  |  |  |             } else if l.0 == cleared_line_number { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |                 (Y_MAX - (idx + 1) as u32) as usize | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         let original_cleared_lines_len = cleared_lines.len(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         // Iterate over all lines in reverse sorted order (largest to smallest)
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         lines |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             .iter_mut() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             .sorted_by(|i, j| i.0.cmp(&j.0)) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             .rev() |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             .for_each(|mut l| { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 // If the current index is in the set of cleared lines, move it to the top
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 // Otherwise, move it down by the number of cleared lines
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if cleared_lines.contains(&l.0) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     // Move to the N-offset line number (top, top-1, etc)
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     let offset = original_cleared_lines_len - cleared_lines.len(); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     info!("Moving line {:?}->{:?}", l.0, Y_MAX - 1 - offset); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     l.0 = Y_MAX - 1 - offset; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     cleared_lines.pop(); |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } else { |  |  |  |             } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |                     info!("Moving line {:?}->{:?}", l.0, l.0 - cleared_lines.len()); |  |  |  |                 l.0 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                     l.0 -= cleared_lines.len(); |  |  |  |             }; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |             info!("Moving line {:?} to {:?}", l, dest); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             l.0 = dest; | 
			
		
	
		
		
			
				
					
					|  |  |  |         }); |  |  |  |         }); | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | @ -693,7 +614,7 @@ fn adjust_block_lines( | 
			
		
	
		
		
			
				
					
					|  |  |  |     query.iter().for_each(|(e, Line(i))| { |  |  |  |     query.iter().for_each(|(e, Line(i))| { | 
			
		
	
		
		
			
				
					
					|  |  |  |         parent.iter_descendants(e).for_each(|block| { |  |  |  |         parent.iter_descendants(e).for_each(|block| { | 
			
		
	
		
		
			
				
					
					|  |  |  |             if let Ok(mut gp) = blocks.get_mut(block) { |  |  |  |             if let Ok(mut gp) = blocks.get_mut(block) { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 gp.y = *i; |  |  |  |                 gp.y = *i as u32; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         }); |  |  |  |         }); | 
			
		
	
		
		
			
				
					
					|  |  |  |     }); |  |  |  |     }); | 
			
		
	
	
		
		
			
				
					|  |  | @ -725,7 +646,7 @@ fn movement( | 
			
		
	
		
		
			
				
					
					|  |  |  |             Movement::Right => (center.with_offset(1, 0), *this_shape), |  |  |  |             Movement::Right => (center.with_offset(1, 0), *this_shape), | 
			
		
	
		
		
			
				
					
					|  |  |  |             Movement::Rotate => (Ok(*center), this_shape.rotated()), |  |  |  |             Movement::Rotate => (Ok(*center), this_shape.rotated()), | 
			
		
	
		
		
			
				
					
					|  |  |  |         }; |  |  |  |         }; | 
			
		
	
		
		
			
				
					
					|  |  |  |         debug!( |  |  |  |         info!( | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             "Proposed change: {:?}\n{}", |  |  |  |             "Proposed change: {:?}\n{}", | 
			
		
	
		
		
			
				
					
					|  |  |  |             new_center, |  |  |  |             new_center, | 
			
		
	
		
		
			
				
					
					|  |  |  |             new_shape.as_ascii() |  |  |  |             new_shape.as_ascii() | 
			
		
	
	
		
		
			
				
					|  |  | @ -777,50 +698,112 @@ fn movement( | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn check_line_removal( |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     mut events: RemovedComponents<Line>, |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | ) { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     events.read().for_each(|e| { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         info!("Line entity {:?} removed", e); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     }); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | // TODO: Just despawn?
 |  |  |  | // TODO: Just despawn?
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn deactivate_shape( |  |  |  | fn deactive_shape( | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     mut events: RemovedComponents<Shape>, |  |  |  |     trigger: Trigger<OnRemove, Shape>, | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     grid_positions: Query<&GridPosition>, |  |  |  |     grid_positions: Query<&GridPosition>, | 
			
		
	
		
		
			
				
					
					|  |  |  |     parent: Query<&ShapeBlocks>, |  |  |  |     parent: Query<&ShapeBlocks>, | 
			
		
	
		
		
			
				
					
					|  |  |  |     lines: Query<(Entity, &Line), With<LineBlocks>>, |  |  |  |     lines: Query<(Entity, &Line), With<LineBlocks>>, | 
			
		
	
		
		
			
				
					
					|  |  |  |     mut commands: Commands, |  |  |  |     mut commands: Commands, | 
			
		
	
		
		
			
				
					
					|  |  |  | ) { |  |  |  | ) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     events.read().for_each(|target| { |  |  |  |     parent.iter_descendants(trigger.target()).for_each(|block| { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         parent.iter_descendants(target).for_each(|block| { |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         let GridPosition { y, .. } = grid_positions.get(block).unwrap(); |  |  |  |         let GridPosition { y, .. } = grid_positions.get(block).unwrap(); | 
			
		
	
		
		
			
				
					
					|  |  |  |         let parent_line = lines |  |  |  |         let parent_line = lines | 
			
		
	
		
		
			
				
					
					|  |  |  |             .iter() |  |  |  |             .iter() | 
			
		
	
		
		
			
				
					
					|  |  |  |                 .find_map(|(e, Line(i))| (*y == *i).then_some(e)) |  |  |  |             .find_map(|(e, Line(i))| (*y == *i as u32).then_some(e)) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 .unwrap(); // TODO: This crashed once kinda late in a game... why?
 |  |  |  |             .unwrap(); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         commands |  |  |  |         commands | 
			
		
	
		
		
			
				
					
					|  |  |  |             .entity(parent_line) |  |  |  |             .entity(parent_line) | 
			
		
	
		
		
			
				
					
					|  |  |  |             .add_one_related::<LineBlock>(block); |  |  |  |             .add_one_related::<LineBlock>(block); | 
			
		
	
		
		
			
				
					
					|  |  |  |     }); |  |  |  |     }); | 
			
		
	
		
		
			
				
					
					|  |  |  |         commands.entity(target).despawn(); |  |  |  |     commands.entity(trigger.target()).despawn(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     }); |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | fn update_next_shapes(mut buffer: ResMut<ShapesBuffer>) { |  |  |  | #[cfg(test)] | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     // If the buffer contains less than n+1 shapes (where n is the number of possible shapes)
 |  |  |  | mod test { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     // Ideally we have between 1n and 2n shapes in the `next` buffer
 |  |  |  |     use super::*; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     while buffer.next.len() < 8 { |  |  |  | 
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         // TODO: Shuffle these!
 |  |  |  |     #[test] | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         buffer.next.extend([ |  |  |  |     fn test_shape_t() { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Shape::new_o(), |  |  |  |         let mut shape = Shape::new_t(); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Shape::new_t(), |  |  |  | 
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Shape::new_l(), |  |  |  |         let expected_up = "010\n\ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Shape::new_j(), |  |  |  |                            111\n\ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Shape::new_s(), |  |  |  |                            000\n"; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Shape::new_z(), |  |  |  | 
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |             Shape::new_i(), |  |  |  |         let expected_right = "010\n\ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]); |  |  |  |                               011\n\ | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                               010\n"; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let expected_down = "000\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              111\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              010\n"; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let expected_left = "010\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              110\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              010\n"; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_up); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_right); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_down); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_left); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_up); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     #[test] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     fn test_shape_i() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let mut shape = Shape::new_i(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let expected_up = "0010\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                            0010\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                            0010\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                            0010\n"; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let expected_right = "0000\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                               0000\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                               1111\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                               0000\n"; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let expected_down = "0100\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              0100\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              0100\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              0100\n"; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let expected_left = "0000\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              1111\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              0000\n\ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              0000\n"; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_up); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_right); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_down); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_left); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         shape.rotate(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(shape.as_ascii(), expected_up); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     #[test] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     fn test_coordinates() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let shape = Shape::new_t(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let center = GridPosition { x: 5, y: 5 }; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let expected: Vec<Result<GridPosition, GameError>> = vec![ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Ok((5, 6).into()), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Ok((4, 5).into()), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Ok((5, 5).into()), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |             Ok((6, 5).into()), | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ]; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         let actual: Vec<Result<GridPosition, GameError>> = shape.coordinates(¢er).collect(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         assert_eq!(actual, expected); | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
	
		
		
			
				
					|  |  | 
 |