diff --git a/src/bin/tetris/main.rs b/src/bin/tetris/main.rs index ff5ff37..1cd2607 100644 --- a/src/bin/tetris/main.rs +++ b/src/bin/tetris/main.rs @@ -39,15 +39,11 @@ fn main() { init_battler, ), ) - .add_systems( - OnEnter(DebuggingState::On), toggle_art - ) - .add_systems( - OnExit(DebuggingState::On), toggle_art - ) + .add_systems(OnEnter(DebuggingState::On), toggle_art) + .add_systems(OnExit(DebuggingState::On), toggle_art) .add_systems( Update, - game_state_machine.run_if(state_changed::) + game_state_machine.run_if(state_changed::), ) // Input and basic systems .add_systems( @@ -70,12 +66,17 @@ fn main() { .run_if(in_state(GameState::Play)) .run_if(on_timer(Duration::from_secs(1))), movement.run_if(on_message::), - update_position.run_if(any_component_changed::), + update_line_position.run_if(any_component_changed::), + update_block_position.run_if(any_component_changed::), update_shape_blocks - .run_if(any_component_changed::.or(any_component_changed::)) - .after(update_position), + .run_if( + any_component_changed:: + .or(any_component_changed::), + ) + .after(update_block_position), deactivate_shape .run_if(any_component_removed::) + .after(game_state_machine) .after(update_shape_blocks), // Clearing lines systems clear_line.run_if(any_component_changed::), @@ -146,6 +147,14 @@ struct LineBlock { #[derive(Component, Debug, Clone, Copy)] struct Line(usize); +impl From<&Line> for Vec3 { + fn from(Line(y): &Line) -> Vec3 { + let y_0 = -SCALE * 10.0 + (0.5 * SCALE); + let y = y_0 + ((*y as f32) * SCALE); + Vec3::new(0.0, y, 0.0) + } +} + // Just marks a block either of a shape or line #[derive(Component, Debug)] struct Block; @@ -277,9 +286,9 @@ fn game_state_machine( // Set goal based on current goal next.set(GameState::Play); - }, - GameState::Play => (), // handled by user input - GameState::Pause => (), // handled by user input + } + GameState::Play => (), // handled by user input + GameState::Pause => (), // handled by user input GameState::LevelComplete => (), // hanled by user input GameState::NextLevel => { // Increase goal @@ -298,7 +307,7 @@ fn game_state_machine( // Progress to play next.set(GameState::Play) } - GameState::GameOver => (),// handled by user input + GameState::GameOver => (), // handled by user input } } @@ -316,7 +325,7 @@ impl Default for LevelGoal { impl Display for LevelGoal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - LevelGoal::Score(n) => write!(f, "{n}") + LevelGoal::Score(n) => write!(f, "{n}"), } } } @@ -337,7 +346,7 @@ struct Visuals { /// Stores the art assets that may be toggled on and off for development #[derive(Component, Debug, Clone)] struct Art { - texture: Handle + texture: Handle, } #[derive(Resource, Debug, Default)] @@ -446,7 +455,23 @@ fn init_tetris( }); // Spawn line for holding blocks - commands.spawn((Line(y), LineBlocks::default(), TETRIS)); + commands + .spawn(( + Line(y), + LineBlocks::default(), + TETRIS, + Transform::default(), + Visibility::default(), + )) + .with_children(|parent| { + // Spawn a line number next to the line so we can track it + parent.spawn(( + Text2d(format!("{}", y)), + TETRIS, + Transform::from_xyz(0.0, 0.0, 99.0), + Visibility::default(), + )); + }); }); } @@ -646,41 +671,50 @@ fn init_ui(mut commands: Commands, output_images: Res, images: Res }); }); - commands - .spawn(( - Node { - align_self: AlignSelf::Center, - justify_self: JustifySelf::Center, - ..default() - }, - ZIndex(1), - GameState::Pause, - children![(Text::new("paused"), TextColor(WHITE.into()), GameState::Pause)], - )); - - commands - .spawn(( - Node { - align_self: AlignSelf::Center, - justify_self: JustifySelf::Center, - ..default() - }, - ZIndex(1), - GameState::LevelComplete, - children![(Text::new("you did it!"), TextColor(WHITE.into()), GameState::LevelComplete)], - )); - - commands - .spawn(( - Node { - align_self: AlignSelf::Center, - justify_self: JustifySelf::Center, - ..default() - }, - ZIndex(1), - GameState::GameOver, - children![(Text::new("aww, you suck. i'm sorry."), TextColor(WHITE.into()), GameState::GameOver)], - )); + commands.spawn(( + Node { + align_self: AlignSelf::Center, + justify_self: JustifySelf::Center, + ..default() + }, + ZIndex(1), + GameState::Pause, + children![( + Text::new("paused"), + TextColor(WHITE.into()), + GameState::Pause + )], + )); + + commands.spawn(( + Node { + align_self: AlignSelf::Center, + justify_self: JustifySelf::Center, + ..default() + }, + ZIndex(1), + GameState::LevelComplete, + children![( + Text::new("you did it!"), + TextColor(WHITE.into()), + GameState::LevelComplete + )], + )); + + commands.spawn(( + Node { + align_self: AlignSelf::Center, + justify_self: JustifySelf::Center, + ..default() + }, + ZIndex(1), + GameState::GameOver, + children![( + Text::new("aww, you suck. i'm sorry."), + TextColor(WHITE.into()), + GameState::GameOver + )], + )); commands .spawn(( @@ -815,11 +849,7 @@ impl ShapeLayout { } fn new_t() -> Self { - vec![ - vec![0, 1, 0], - vec![1, 1, 1], - vec![0, 0, 0] - ].into() + vec![vec![0, 1, 0], vec![1, 1, 1], vec![0, 0, 0]].into() } fn new_l() -> Self { @@ -923,7 +953,7 @@ impl ShapeLayout { } } -fn update_position( +fn update_block_position( mut changed: Query< (Entity, &GridPosition, &mut Transform), Or<(Added, Changed)>, @@ -941,13 +971,25 @@ fn update_position( }); } +fn update_line_position( + mut changed: Query<(Entity, &Line, &mut Transform), Or<(Added, Changed)>>, +) { + changed.iter_mut().for_each(|(e, gp, mut t)| { + let v3: Vec3 = gp.into(); + debug!( + "Updating {e} with grid position {:?} to coordinates {:?}", + gp, v3 + ); + + t.translation.x = v3.x; + t.translation.y = v3.y; + }); +} + fn update_shape_blocks( query: Query< (Entity, &ShapeLayout, &GridPosition), - Or<( - Changed, - Changed, - )>, + Or<(Changed, Changed)>, >, mut blocks: Query<&mut GridPosition, (With, Without)>, ) { @@ -957,14 +999,12 @@ fn update_shape_blocks( debug_assert!(!blocks.is_empty()); let mut p = sl.coordinates_at(center); - blocks.iter_mut().for_each(|mut gp| { - match p.next() { - Some(next) => match next { - Ok(next_gp) => *gp = next_gp, - Err(_) => warn!("Next coordinate was an Err?"), - }, - None => warn!("Next coordinate was a None?"), - } + blocks.iter_mut().for_each(|mut gp| match p.next() { + Some(next) => match next { + Ok(next_gp) => *gp = next_gp, + Err(_) => warn!("Next coordinate was an Err?"), + }, + None => warn!("Next coordinate was a None?"), }); }); } @@ -991,7 +1031,14 @@ fn on_add_shape_layout( .entity(e) .with_related_entities::(|parent| { sl.coordinates_at(center).for_each(|gp| { - parent.spawn((mesh.clone(), MeshMaterial2d(mat.clone()), art.clone(), gp.unwrap(), Block, TETRIS)); + parent.spawn(( + mesh.clone(), + MeshMaterial2d(mat.clone()), + art.clone(), + gp.unwrap(), + Block, + TETRIS, + )); }); }); } @@ -1052,17 +1099,17 @@ fn kb_input( }, GameState::Pause => match key_code { KeyCode::Escape | KeyCode::Enter => next.set(GameState::Play), - _ => () - } + _ => (), + }, GameState::GameOver => match key_code { KeyCode::Escape => todo!("Quit the game"), KeyCode::Enter => next.set(GameState::NewGame), - _ => () + _ => (), }, GameState::LevelComplete => match key_code { KeyCode::Escape => todo!("Quit the game"), KeyCode::Enter => next.set(GameState::NextLevel), - _ => () + _ => (), }, GameState::NewGame | GameState::NextLevel => (), } @@ -1243,15 +1290,15 @@ fn movement( // For each of the proposed positions for position in new_positions { if let Ok(new_center) = position { - let new_blocks: Vec> = new_shape_layout - .coordinates_at(&new_center) - .collect(); + let new_blocks: Vec> = + new_shape_layout.coordinates_at(&new_center).collect(); // If the move would cause one block to be out of bounds, // Not a valid move so return. if new_blocks.contains(&Err(OutOfBoundsError::Left)) - || new_blocks.contains(&Err(OutOfBoundsError::Right)) { - return + || new_blocks.contains(&Err(OutOfBoundsError::Right)) + { + return; } // If would be out of bounds at the bottom of play @@ -1274,10 +1321,9 @@ fn movement( next_state.set(GameState::GameOver); } // We are moving down - if [ - MovementDirection::Down, - MovementDirection::Skip, - ].contains(&message.direction) { + if [MovementDirection::Down, MovementDirection::Skip] + .contains(&message.direction) + { // De-activate this piece commands.entity(message.entity).remove::(); } @@ -1374,16 +1420,26 @@ fn update_next_shapes(mut buffer: ResMut) { } fn assert_grid_position_uniqueness( - grid_positions: Query<(Entity, &GridPosition, Option<&LineBlock>, Option<&ShapeBlock>), With>, + grid_positions: Query< + ( + Entity, + &GridPosition, + Option<&LineBlock>, + Option<&ShapeBlock>, + ), + With, + >, ) { - grid_positions.iter_combinations().for_each(|[(e1, a, lb1, sb1), (e2, b, lb2, sb2)]| { - if a == b { - error!("Entities {e1:?} and {e2:?} @ GP {a:?}/{b:?}!"); - error!("\t{:?}: (Block: {:?}| Line: {:?})!", e1, sb1, lb1); - error!("\t{:?}: (Block: {:?}| Line: {:?})!", e2, sb2, lb2); - } - assert_ne!(a, b, "Two entities are in the same grid position!"); - }); + grid_positions + .iter_combinations() + .for_each(|[(e1, a, lb1, sb1), (e2, b, lb2, sb2)]| { + if a == b { + error!("Entities {e1:?} and {e2:?} @ GP {a:?}/{b:?}!"); + error!("\t{:?}: (Block: {:?}| Line: {:?})!", e1, sb1, lb1); + error!("\t{:?}: (Block: {:?}| Line: {:?})!", e2, sb2, lb2); + } + assert_ne!(a, b, "Two entities are in the same grid position!"); + }); } fn sync_health( diff --git a/src/bin/tetris/test.rs b/src/bin/tetris/test.rs index 9b60d6c..2c58d7c 100644 --- a/src/bin/tetris/test.rs +++ b/src/bin/tetris/test.rs @@ -149,12 +149,9 @@ fn test_shape_block_layout_rotation() { fn test_shape_block_center() { { let actual = ShapeBlockLayout { - inner: vec![ - vec![0], - vec![0], - vec![0], - ] - }.center(); + inner: vec![vec![0], vec![0], vec![0]], + } + .center(); let expected = (0, 1); @@ -163,13 +160,9 @@ fn test_shape_block_center() { { let actual = ShapeBlockLayout { - inner: vec![ - vec![0], - vec![0], - vec![0], - vec![0], - ] - }.center(); + inner: vec![vec![0], vec![0], vec![0], vec![0]], + } + .center(); let expected = (0, 2); @@ -178,11 +171,9 @@ fn test_shape_block_center() { { let actual = ShapeBlockLayout { - inner: vec![ - vec![0, 0], - vec![0, 0], - ] - }.center(); + inner: vec![vec![0, 0], vec![0, 0]], + } + .center(); let expected = (0, 1); @@ -191,12 +182,9 @@ fn test_shape_block_center() { { let actual = ShapeBlockLayout { - inner: vec![ - vec![0, 0, 0], - vec![0, 0, 0], - vec![0, 0, 0], - ] - }.center(); + inner: vec![vec![0, 0, 0], vec![0, 0, 0], vec![0, 0, 0]], + } + .center(); let expected = (1, 1); @@ -210,8 +198,9 @@ fn test_shape_block_center() { vec![0, 0, 0, 0], vec![0, 0, 0, 0], vec![0, 0, 0, 0], - ] - }.center(); + ], + } + .center(); let expected = (1, 2); diff --git a/src/lib.rs b/src/lib.rs index f719abc..f6f917c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,16 +13,11 @@ mod ui; mod version; // Rust stdlib -pub use std::{ - collections::VecDeque, - f32::consts::PI, - fmt::Display, -}; pub use core::time::Duration; +pub use std::{collections::VecDeque, f32::consts::PI, fmt::Display}; // Community libraries pub use bevy::{ - time::common_conditions::*, asset::{ AssetLoader, AssetMetaCheck, LoadContext, LoadState, LoadedFolder, RenderAssetUsages, io::Reader, uuid_handle, @@ -44,6 +39,7 @@ pub use bevy::{ reflect::TypePath, render::render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, sprite_render::*, + time::common_conditions::*, window::{WindowResized, WindowResolution}, }; pub use itertools::Itertools;