diff --git a/src/bin/tetris/main.rs b/src/bin/tetris/main.rs index 724be0a..d7711af 100644 --- a/src/bin/tetris/main.rs +++ b/src/bin/tetris/main.rs @@ -3,7 +3,7 @@ use games::*; -// TODO: Detect when piece is going to go out of bounds and restirct parent from moving there +// *TODO: Detect when piece is going to go out of bounds and restirct parent from moving there // TODO: When shape touches the rest of the pieces, re-parent to line entity // TODO: When line is "full" (has 10 children) clear line and add to score @@ -16,7 +16,7 @@ fn main() { ..default() }) .init_state::() - .add_systems(Startup, (init_pieces, init_debug_ui)) + .add_systems(Startup, (init_world, init_debug_ui)) .add_systems( Update, ( @@ -40,6 +40,7 @@ fn main() { sync_singleton_to_ui::.run_if(any_component_changed::), check_collision.run_if(any_component_changed::), add_piece.run_if(not(any_with_component::)), + clear_line.run_if(any_component_changed::), ), ) .add_systems(Update, draw_grid) @@ -81,17 +82,35 @@ impl Display for Shape { // The blocks making up this shape #[derive(Component)] -#[relationship_target(relationship = BlockOf)] +#[relationship_target(relationship = ShapeBlock)] struct ShapeBlocks(Vec); /// A part of a piece, i.e., a single square of a piece #[derive(Component, Debug)] #[relationship(relationship_target = ShapeBlocks)] -struct BlockOf { +struct ShapeBlock { #[relationship] - parent: Entity, + shape: Entity, } +// The blocks making up this shape +#[derive(Component, Default)] +#[relationship_target(relationship = LineBlock)] +struct LineBlocks(Vec); + +/// A part of a piece, i.e., a single square of a piece +#[derive(Component, Debug)] +#[require(Transform, Visibility)] +#[relationship(relationship_target = LineBlocks)] +struct LineBlock { + #[relationship] + line: Entity, +} + +// A line holds up to 10 blocks before being cleared +#[derive(Component, Debug)] +struct Line(u8); + // Just marks a block either of a shape or line #[derive(Component, Debug)] struct Block; @@ -154,7 +173,7 @@ impl GridPosition { } fn is_colliding_with(&self, other: &Self) -> bool { - self.x == other.x && self.y - 1 == other.y + self.x == other.x && self.y.saturating_sub(1) == other.y } fn add_offset(&self, RelativePosition { x: x1, y: y1 }: &RelativePosition) -> Self { @@ -263,7 +282,7 @@ struct Visuals { mesh: Handle, } -fn init_pieces( +fn init_world( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, @@ -275,6 +294,10 @@ fn init_pieces( }), mesh: meshes.add(Rectangle::new(SCALE, SCALE)), }); + + (0..20).for_each(|i| { + commands.spawn((Line(i), LineBlocks::default())); + }); } fn init_debug_ui(mut commands: Commands) { @@ -442,7 +465,7 @@ fn set_piece( Changed, )>, >, - mut blocks: Query<&mut RelativePosition, With>, + mut blocks: Query<&mut RelativePosition, With>, mut commands: Commands, visuals: Res, ) { @@ -456,7 +479,7 @@ fn set_piece( if blocks.is_empty() { commands .entity(e) - .with_related_entities::(|parent| { + .with_related_entities::(|parent| { positions.into_iter().for_each(|rp| { parent.spawn((Mesh2d(mesh.clone()), MeshMaterial2d(mat.clone()), rp, Block)); }); @@ -472,6 +495,7 @@ fn set_piece( fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed>) { query.iter_mut().for_each(|(gp, mut t)| { + debug_assert!(gp.x < X_MAX, "block x > x_max"); t.translation = gp.into(); debug!("Updating position {:?}", t.translation); }); @@ -548,7 +572,7 @@ fn shape_block_movement( Without, ), >, - mut blocks: Query<(&mut GridPosition, &RelativePosition), With>, + mut blocks: Query<(&mut GridPosition, &RelativePosition), With>, ) { shape.iter().for_each(|shape_gp| { blocks.iter_mut().for_each(|(mut block_gp, block_rp)| { @@ -574,8 +598,9 @@ fn clock_cycle(n: f32) -> impl FnMut(Res