diff --git a/src/bin/tetris/main.rs b/src/bin/tetris/main.rs index 357bd34..384c336 100644 --- a/src/bin/tetris/main.rs +++ b/src/bin/tetris/main.rs @@ -10,7 +10,7 @@ use games::*; fn main() { App::new() .add_plugins(BaseGamePlugin { - name: "falling-block-rpg".into(), + name: "falling-block-adventure".into(), target_resolution: (640.0, 480.0).into(), game_type: GameType::Two, ..default() @@ -24,16 +24,11 @@ fn main() { falling .run_if(in_state(Falling::On)) .run_if(clock_cycle(1.0)), - set_shape.run_if( - any_component_added:: - .or(any_component_changed::) - .or(any_component_added::) - .or(any_component_changed::), - ), - update_relative_position, - update_position.after(update_relative_position), + update_shape_blocks + .run_if(any_component_added::.or(any_component_changed::)), sync_singleton_to_ui::.run_if(any_component_changed::), sync_singleton_to_ui::.run_if(any_component_changed::), + update_position, add_piece.run_if(not(any_with_component::)), clear_line.run_if(any_component_changed::), ), @@ -84,37 +79,6 @@ struct Line(u8); #[derive(Component, Debug)] struct Block; -#[derive(Component, Event, Debug, Clone, Copy, PartialEq)] -#[require(GridPosition)] -struct RelativePosition { - x: i8, - y: i8, -} - -impl RelativePosition { - fn up() -> Self { - RelativePosition { x: 0, y: 1 } - } - - fn down() -> Self { - RelativePosition { x: 0, y: -1 } - } - - fn left() -> Self { - RelativePosition { x: -1, y: 0 } - } - - fn right() -> Self { - RelativePosition { x: 1, y: 0 } - } -} - -impl From<(i8, i8)> for RelativePosition { - fn from((x, y): (i8, i8)) -> RelativePosition { - RelativePosition { x, y } - } -} - #[derive(Component, Debug, Clone, Copy, PartialEq)] #[require(Transform, Visibility)] struct GridPosition { @@ -123,68 +87,31 @@ struct GridPosition { } impl GridPosition { - fn move_up(&self) -> Self { - Self { - y: if self.y + 1 < Y_MAX { - self.y.saturating_add(1) - } else { - self.y - }, - x: self.x, - } - } - - fn move_down(&mut self) -> Self { - Self { - y: self.y.saturating_sub(1), - x: self.x, - } - } - - fn move_left(&mut self) -> Self { - Self { - x: self.x.saturating_sub(1), - y: self.y, - } - } + fn with_offset(self, other_x: isize, other_y: isize) -> Result { + let x = self.x as isize + other_x; + let y = self.y as isize + other_y; - fn move_right(&mut self) -> Self { - Self { - x: if self.x + 1 < X_MAX { - self.x.saturating_add(1) - } else { - self.x - }, - y: self.y, - } - } - - fn is_colliding_with(&self, other: &Self) -> bool { - self.x == other.x && self.y.saturating_sub(1) == other.y - } - - fn add_relative( - &self, - RelativePosition { x: x1, y: y1 }: &RelativePosition, - ) -> Result { - let x = self - .x - .checked_add_signed(*x1 as i32) - .ok_or(GameError::OutOfBoundsLeft)?; - let y = self - .y - .checked_add_signed(*y1 as i32) - .ok_or(GameError::OutOfBoundsDown)?; - if x >= X_MAX { - // TODO: y > Y_MAX? + if x > X_MAX as isize { + Err(GameError::OutOfBoundsLeft) + } else if x < 0 { Err(GameError::OutOfBoundsRight) + } else if y < 0 { + Err(GameError::OutOfBoundsDown) } else { - debug!("Moving to {x},{y}"); - Ok(GridPosition { x, y }) + Ok(GridPosition { + x: x as u32, + y: y as u32, + }) } } } +impl Display for GridPosition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({},{})", self.x, self.y) + } +} + #[derive(Error, Debug, PartialEq)] enum GameError { #[error("Coordinates are out of bounds: Left")] @@ -208,7 +135,6 @@ impl From<&GridPosition> for Vec3 { // Grid Positions start in the bottom left of the area // So (0, 0) is the bottom left, (0, 9) is the bottom right, etc - // TODO: Custom offset allowing pieces like O and I to have correct center let x_0 = -SCALE * 5.0 + (0.5 * SCALE); let x = x_0 + ((*x as f32) * SCALE); @@ -336,7 +262,7 @@ fn init_debug_ui(mut commands: Commands) { }); } -#[derive(Component, Debug)] +#[derive(Component, Debug, Clone, Copy)] enum Shape { M4(Mat4), M3(Mat3), @@ -365,63 +291,63 @@ impl Shape { fn new_o() -> Self { Self::from_mat4(Mat4::from_cols_array_2d(&[ - [0.,0.,0.,0.], - [0.,1.,1.,0.], - [0.,1.,1.,0.], - [0.,0.,0.,0.], + [0., 0., 0., 0.], + [0., 1., 1., 0.], + [0., 1., 1., 0.], + [0., 0., 0., 0.], ])) } fn new_t() -> Self { Self::from_mat3(Mat3::from_cols_array_2d(&[ - [0.,1.,0.], - [1.,1.,1.], - [0.,0.,0.], + [0., 1., 0.], + [1., 1., 1.], + [0., 0., 0.], ])) } fn new_l() -> Self { Self::from_mat4(Mat4::from_cols_array_2d(&[ - [0.,0.,0.,0.], - [0.,1.,0.,0.], - [0.,1.,0.,0.], - [0.,1.,1.,0.], + [0., 0., 0., 0.], + [0., 1., 0., 0.], + [0., 1., 0., 0.], + [0., 1., 1., 0.], ])) } fn new_j() -> Self { Self::from_mat4(Mat4::from_cols_array_2d(&[ - [0.,0.,0.,0.], - [0.,0.,1.,0.], - [0.,0.,1.,0.], - [0.,1.,1.,0.], + [0., 0., 0., 0.], + [0., 0., 1., 0.], + [0., 0., 1., 0.], + [0., 1., 1., 0.], ])) } fn new_s() -> Self { Self::from_mat4(Mat4::from_cols_array_2d(&[ - [0.,0.,0.,0.], - [0.,1.,1.,0.], - [1.,1.,0.,0.], - [0.,0.,0.,0.], + [0., 0., 0., 0.], + [0., 1., 1., 0.], + [1., 1., 0., 0.], + [0., 0., 0., 0.], ])) } fn new_z() -> Self { Self::from_mat4(Mat4::from_cols_array_2d(&[ - [0.,0.,0.,0.], - [1.,1.,0.,0.], - [0.,1.,1.,0.], - [0.,0.,0.,0.], + [0., 0., 0., 0.], + [1., 1., 0., 0.], + [0., 1., 1., 0.], + [0., 0., 0., 0.], ])) } fn new_i() -> Self { Self::from_mat4(Mat4::from_cols_array_2d(&[ - [0.,0.,1.,0.], - [0.,0.,1.,0.], - [0.,0.,1.,0.], - [0.,0.,1.,0.], + [0., 0., 1., 0.], + [0., 0., 1., 0.], + [0., 0., 1., 0.], + [0., 0., 1., 0.], ])) } @@ -452,16 +378,18 @@ impl Shape { *self = self.rotated(); } - // TODO: return impl Iterator - fn relative_coordinates(&self) -> impl Iterator { - let mut v: Vec = Vec::new(); + fn coordinates( + &self, + center: &GridPosition, + ) -> impl Iterator> { + let mut v: Vec> = Vec::new(); match self { Self::M4(inner) => { for (i, y) in (-1..3).rev().enumerate() { let c = inner.col(i); for (j, x) in (-1..3).enumerate() { if c[j] == 1.0 { - v.push(RelativePosition { x, y }); + v.push(center.with_offset(x, y)); } } } @@ -471,7 +399,7 @@ impl Shape { let c = inner.col(i); for (j, x) in (-1..2).enumerate() { if c[j] == 1.0 { - v.push(RelativePosition { x, y }); + v.push(center.with_offset(x, y)); } } } @@ -480,10 +408,6 @@ impl Shape { v.into_iter() } - fn coordinates(&self, center: &GridPosition) -> impl Iterator> { - self.relative_coordinates().map(|rp| center.add_relative(&rp)) - } - fn as_ascii(&self) -> String { let mut output = String::default(); @@ -493,52 +417,19 @@ impl Shape { let col = this.col(i).to_array(); output += format!("{}{}{}{}\n", col[0], col[1], col[2], col[3]).as_str(); } - }, + } Self::M3(this) => { for i in 0..3 { let col = this.col(i).to_array(); output += format!("{}{}{}\n", col[0], col[1], col[2]).as_str(); } - }, + } }; output } } -fn set_shape( - query: Query< - (Entity, &Shape, &Orientation), - Or<(Added, Changed)>, - >, - mut blocks: Query<&mut RelativePosition, With>, - mut commands: Commands, - visuals: Res, -) { - query.iter().for_each(|(e, s, o)| { - debug!("Setting piece: {e:?} {s:?} {o:?}"); - - if blocks.is_empty() { - let mesh = Mesh2d(visuals.mesh.clone()); - let mat = MeshMaterial2d(visuals.material.clone()); - commands - .entity(e) - .with_related_entities::(|parent| { - s.relative_coordinates().for_each(|rp| { - parent - .spawn((mesh.clone(), mat.clone(), rp, Block)) - .observe(movement); - }); - }); - } else { - let mut p = s.relative_coordinates(); - blocks.iter_mut().for_each(|mut rp| { - *rp = p.next().unwrap(); - }); - } - }); -} - fn update_position( mut changed: Query< (Entity, &GridPosition, &mut Transform), @@ -552,29 +443,38 @@ fn update_position( gp, v3 ); - debug_assert!(gp.x < X_MAX, "block x > x_max"); t.translation = gp.into(); }); } -fn update_relative_position( - shape: Single<&GridPosition, With>, - mut query: Query< - (Entity, &mut GridPosition, &RelativePosition), - ( - Without, - Or<(Added, Changed)>, - ), - >, +// TODO: Inline this to when movement occurs +fn update_shape_blocks( + query: Query<(Entity, &Shape, &Orientation, &GridPosition), Or<(Added, Changed)>>, + mut blocks: Query<&mut GridPosition, (With, Without)>, + mut commands: Commands, + visuals: Res, ) { - query.iter_mut().for_each(|(e, mut gp, rp)| { - debug!( - "Updating {e} grid position to {:?} + {:?} = {:?}", - gp, - rp, - gp.add_relative(rp).unwrap() - ); - *gp = (*shape).add_relative(rp).unwrap(); + query.iter().for_each(|(e, s, o, center)| { + info!("Setting piece: {e:?} {o:?} {center:?}\n{}", s.as_ascii()); + + if blocks.is_empty() { + let mesh = Mesh2d(visuals.mesh.clone()); + let mat = MeshMaterial2d(visuals.material.clone()); + commands + .entity(e) + .with_related_entities::(|parent| { + s.coordinates(center).for_each(|gp| { + parent + .spawn((mesh.clone(), mat.clone(), gp.unwrap(), Block)) + .observe(movement); + }); + }); + } else { + let mut p = s.coordinates(center); + blocks.iter_mut().for_each(|mut gp| { + *gp = p.next().unwrap().unwrap(); + }); + } }); } @@ -590,23 +490,21 @@ fn kb_input( key_code, state, .. }| { if let ButtonState::Pressed = state { - // TODO: Restict movement based on size/orientation of piece - // Check if children would be outside play area... query.iter_mut().for_each(|(e, o, mut s)| { match key_code { // Up arrow should rotate if in falling mode // Only move up if in falling::off mode KeyCode::ArrowUp => { - commands.entity(e).trigger(o.prev()); + commands.entity(e).trigger(Movement::Rotate); } KeyCode::ArrowDown => { - commands.entity(e).trigger(RelativePosition::down()); + commands.entity(e).trigger(Movement::Down); } KeyCode::ArrowLeft => { - commands.entity(e).trigger(RelativePosition::left()); + commands.entity(e).trigger(Movement::Left); } KeyCode::ArrowRight => { - commands.entity(e).trigger(RelativePosition::right()); + commands.entity(e).trigger(Movement::Right); } KeyCode::Space => next.set(match curr.get() { Falling::On => Falling::Off, @@ -641,7 +539,7 @@ fn draw_grid(mut gizmos: Gizmos) { fn falling(mut shape: Query>, mut commands: Commands) { shape.iter_mut().for_each(|e| { info!("Making {:?} fall", e); - commands.entity(e).trigger(RelativePosition::down()); + commands.entity(e).trigger(Movement::Down); }); } @@ -662,9 +560,12 @@ fn clock_cycle(n: f32) -> impl FnMut(Res