Fixed line clearing bug

main
Elijah Voigt 1 day ago
parent 728e36171b
commit a6c96a6588

@ -1,3 +1,4 @@
#![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)]
@ -27,39 +28,44 @@ fn main() {
.add_systems(Startup, (init_world, init_debug_ui, init_ui)) .add_systems(Startup, (init_world, init_debug_ui, init_ui))
// Input and basic systems // 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>), toggle_state_visibility::<GameState>.run_if(state_changed::<GameState>),
) ),
) )
.add_systems( .add_systems(
Update, Update,
( (
update_next_shapes.run_if(resource_changed::<ShapesBuffer>.or(resource_added::<ShapesBuffer>)), update_next_shapes
add_piece.run_if(not(any_with_component::<Shape>)).after(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 update_shape_blocks
.run_if(any_component_added::<Shape>.or(any_component_changed::<Shape>)), .run_if(any_component_added::<Shape>.or(any_component_changed::<Shape>)),
falling falling
.run_if(in_state(GameState::Falling)) .run_if(in_state(GameState::Falling))
.run_if(clock_cycle(1.0)), .run_if(clock_cycle(1.0)),
update_position.run_if(any_component_changed::<GridPosition>), update_position.run_if(any_component_changed::<GridPosition>),
deactivate_shape.run_if(any_component_removed::<Shape>), deactivate_shape.run_if(any_component_removed::<Shape>),
check_line_removal,
// Clearing lines systems // Clearing lines systems
clear_line.run_if(any_component_changed::<LineBlocks>), clear_line.run_if(any_component_changed::<LineBlocks>),
adjust_block_lines.run_if(any_component_changed::<Line>).after(clear_line), adjust_block_lines
.run_if(any_component_changed::<Line>)
.after(clear_line),
), ),
) )
// UI systems // UI systems
.add_systems(Update, .add_systems(
Update,
( (
sync_resource_to_ui::<ShapesBuffer>.run_if(resource_changed::<ShapesBuffer>), sync_resource_to_ui::<ShapesBuffer>.run_if(resource_changed::<ShapesBuffer>),
sync_resource_to_ui::<Score>.run_if(resource_changed::<Score>), 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>), sync_singleton_to_ui::<Orientation>.run_if(any_component_changed::<Orientation>),
) ),
) )
.add_systems(Update, draw_grid) .add_systems(Update, draw_grid)
.run(); .run();
@ -304,31 +310,25 @@ fn init_ui(mut commands: Commands) {
BackgroundColor(BLACK.into()), BackgroundColor(BLACK.into()),
)) ))
.with_children(|parent| { .with_children(|parent| {
parent.spawn(( parent
Node { .spawn((Node {
flex_direction: FlexDirection::Column, flex_direction: FlexDirection::Column,
align_items: AlignItems::Center, align_items: AlignItems::Center,
..default() ..default()
}, },))
)).with_children(|parent|{ .with_children(|parent| {
parent.spawn(Text::new("Next:")); parent.spawn(Text::new("Next:"));
parent.spawn(( parent.spawn((Text::new("???"), SyncResource::<ShapesBuffer>::default()));
Text::new("???"),
SyncResource::<ShapesBuffer>::default(),
));
}); });
parent.spawn(( parent
Node { .spawn((Node {
flex_direction: FlexDirection::Column, flex_direction: FlexDirection::Column,
align_items: AlignItems::Center, align_items: AlignItems::Center,
..default() ..default()
}, },))
)).with_children(|parent|{ .with_children(|parent| {
parent.spawn(Text::new("Score:")); parent.spawn(Text::new("Score:"));
parent.spawn(( parent.spawn((Text::new("???"), SyncResource::<Score>::default()));
Text::new("???"),
SyncResource::<Score>::default(),
));
}); });
}); });
@ -563,7 +563,7 @@ fn update_shape_blocks(
visuals: Res<Visuals>, visuals: Res<Visuals>,
) { ) {
query.iter().for_each(|(e, s, o, center)| { query.iter().for_each(|(e, s, o, center)| {
info!("Setting piece: {e:?} {o:?} {center:?}\n{}", s.as_ascii()); debug!("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());
@ -678,16 +678,17 @@ 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<(Entity, &LineBlocks, &mut Line)>, mut lines: Query<&mut Line>,
line_blocks: Query<&LineBlocks>,
mut score: ResMut<Score>, mut score: ResMut<Score>,
mut commands: Commands, mut commands: Commands,
) { ) {
let cleared_lines: Vec<usize> = changed_lines let mut cleared_lines: Vec<usize> = changed_lines
.iter() .iter()
.filter_map(|e| lines.get(e).ok()) .filter_map(|e| try { (Some(e)?, line_blocks.get(e).ok()?, 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>(); commands.entity(e).despawn_related::<LineBlocks>().insert(LineBlocks::default());
score.0 += 1; score.0 += 1;
info!("New score: {:?}", score.0); info!("New score: {:?}", score.0);
Some(*i) Some(*i)
@ -695,23 +696,41 @@ fn clear_line(
None None
} }
}) })
.sorted()
.collect(); .collect();
if !cleared_lines.is_empty() {
info!("Cleared lines: {:?}", cleared_lines); info!("Cleared lines: {:?}", cleared_lines);
for (idx, cleared_line_number) in cleared_lines.into_iter().sorted().enumerate() { #[cfg(debug_assertions)]
info!("Processing line {cleared_line_number} ({idx})"); {
let cleared_line_number = cleared_line_number - idx; debug_assert_eq!(lines.iter().count(), 20, "There should be 20 lines");
lines.iter_mut().sorted_by(|(_, _, i), (_, _, j)| i.0.cmp(&j.0)).for_each(|(_, _, mut l)| { // Check that all line numbers are present
let dest = if l.0 > cleared_line_number { lines.iter().map(|Line(i)| i).sorted().enumerate().for_each(|(i, line_num)| {
l.0 - 1 debug_assert_eq!(i, *line_num, "Line numbers should match their sorted index");
} else if l.0 == cleared_line_number { });
Y_MAX - 1 }
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 {
l.0 info!("Moving line {:?}->{:?}", l.0, l.0 - cleared_lines.len());
}; l.0 -= cleared_lines.len();
info!("Moving line {:?} to {:?}", l, dest); }
l.0 = dest;
}); });
} }
} }
@ -721,17 +740,6 @@ fn adjust_block_lines(
parent: Query<&LineBlocks>, parent: Query<&LineBlocks>,
mut blocks: Query<&mut GridPosition>, mut blocks: Query<&mut GridPosition>,
) { ) {
#[cfg(debug_assertions)]
{
// Check that all line numbers are present
let expected_line_numbers = 0..Y_MAX;
let actual_line_numbers = query.iter().map(|(_, Line(i))| i).sorted();
query.iter().map(|(_, Line(i))| i).sorted().for_each(|i| info!("Line #: {i}"));
std::iter::zip(expected_line_numbers, actual_line_numbers).for_each(|(a, b)| {
debug_assert_eq!(a as usize, *b);
});
}
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) {
@ -767,7 +775,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()),
}; };
info!( debug!(
"Proposed change: {:?}\n{}", "Proposed change: {:?}\n{}",
new_center, new_center,
new_shape.as_ascii() new_shape.as_ascii()
@ -819,6 +827,14 @@ 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 deactivate_shape(
mut events: RemovedComponents<Shape>, mut events: RemovedComponents<Shape>,

Loading…
Cancel
Save