|
|
|
|
@ -42,13 +42,13 @@ fn main() {
|
|
|
|
|
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
|
|
|
|
|
.run_if(in_state(GameState::Falling))
|
|
|
|
|
.run_if(clock_cycle(1.0)),
|
|
|
|
|
update_position.run_if(any_component_changed::<GridPosition>),
|
|
|
|
|
deactivate_shape.run_if(any_component_removed::<Shape>),
|
|
|
|
|
update_shape_blocks
|
|
|
|
|
.run_if(any_component_added::<Shape>.or(any_component_changed::<Shape>)).after(update_position),
|
|
|
|
|
deactivate_shape.run_if(any_component_removed::<Shape>).after(update_shape_blocks),
|
|
|
|
|
check_line_removal,
|
|
|
|
|
// Clearing lines systems
|
|
|
|
|
clear_line.run_if(any_component_changed::<LineBlocks>),
|
|
|
|
|
@ -487,8 +487,32 @@ impl Shape {
|
|
|
|
|
|
|
|
|
|
output
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn height(&self) -> usize {
|
|
|
|
|
let mut x = 0;
|
|
|
|
|
|
|
|
|
|
match self {
|
|
|
|
|
Self::M4(this) => {
|
|
|
|
|
for i in 0..4 {
|
|
|
|
|
if this.col(i).to_array().contains(&1.0) {
|
|
|
|
|
x += 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Self::M3(this) => {
|
|
|
|
|
for i in 0..3 {
|
|
|
|
|
if this.col(i).to_array().contains(&1.0) {
|
|
|
|
|
x += 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
x
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: move to trigger
|
|
|
|
|
fn update_position(
|
|
|
|
|
mut changed: Query<
|
|
|
|
|
(Entity, &GridPosition, &mut Transform),
|
|
|
|
|
@ -506,9 +530,9 @@ fn update_position(
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Inline this to when movement occurs
|
|
|
|
|
// TODO: Move to trigger
|
|
|
|
|
fn update_shape_blocks(
|
|
|
|
|
query: Query<(Entity, &Shape, &GridPosition), Or<(Added<Shape>, Changed<Shape>)>>,
|
|
|
|
|
query: Query<(Entity, &Shape, &GridPosition), Or<(Added<Shape>, Changed<Shape>, Added<GridPosition>, Changed<GridPosition>)>>,
|
|
|
|
|
mut blocks: Query<&mut GridPosition, (With<ShapeBlock>, Without<Shape>)>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
visuals: Res<Visuals>,
|
|
|
|
|
@ -565,6 +589,9 @@ fn kb_input(
|
|
|
|
|
KeyCode::ArrowRight => {
|
|
|
|
|
commands.entity(e).trigger(Movement::Right);
|
|
|
|
|
}
|
|
|
|
|
KeyCode::Space => {
|
|
|
|
|
commands.entity(e).trigger(Movement::Skip);
|
|
|
|
|
}
|
|
|
|
|
KeyCode::Escape => next.set(match curr.get() {
|
|
|
|
|
GameState::Falling => GameState::Pause,
|
|
|
|
|
GameState::Pause => GameState::Falling,
|
|
|
|
|
@ -617,10 +644,11 @@ fn clock_cycle(n: f32) -> impl FnMut(Res<Time>, Local<f32>) -> bool {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_piece(mut commands: Commands, mut shapes: ResMut<ShapesBuffer>) {
|
|
|
|
|
let this_shape = shapes.next.pop_front().unwrap();
|
|
|
|
|
commands
|
|
|
|
|
.spawn((
|
|
|
|
|
GridPosition::default(),
|
|
|
|
|
shapes.next.pop_front().unwrap(),
|
|
|
|
|
GridPosition { y: Y_MAX - this_shape.height(), ..default() },
|
|
|
|
|
this_shape,
|
|
|
|
|
))
|
|
|
|
|
.observe(movement);
|
|
|
|
|
}
|
|
|
|
|
@ -705,6 +733,7 @@ enum Movement {
|
|
|
|
|
Left,
|
|
|
|
|
Right,
|
|
|
|
|
Rotate,
|
|
|
|
|
Skip,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: When out of bounds left/right, try to move piece away from wall
|
|
|
|
|
@ -719,57 +748,67 @@ fn movement(
|
|
|
|
|
shape.get_mut(trigger.target()),
|
|
|
|
|
grid_positions.get(trigger.target()),
|
|
|
|
|
) {
|
|
|
|
|
let (new_center, new_shape) = match trigger.event() {
|
|
|
|
|
Movement::Down => (center.with_offset(0, -1), *this_shape),
|
|
|
|
|
Movement::Left => (center.with_offset(-1, 0), *this_shape),
|
|
|
|
|
Movement::Right => (center.with_offset(1, 0), *this_shape),
|
|
|
|
|
Movement::Rotate => (Ok(*center), this_shape.rotated()),
|
|
|
|
|
let new_positions = match trigger.event() {
|
|
|
|
|
Movement::Down => vec![center.with_offset(0, -1)],
|
|
|
|
|
Movement::Left => vec![center.with_offset(-1, 0)],
|
|
|
|
|
Movement::Right => vec![center.with_offset(1, 0)],
|
|
|
|
|
Movement::Rotate => vec![Ok(*center)],
|
|
|
|
|
Movement::Skip => (1..=center.y).map(|i| center.with_offset(0, -(i as isize))).collect(),
|
|
|
|
|
};
|
|
|
|
|
let new_shape = match trigger.event() {
|
|
|
|
|
Movement::Down | Movement::Left | Movement::Right | Movement::Skip => *this_shape,
|
|
|
|
|
Movement::Rotate => this_shape.rotated(),
|
|
|
|
|
};
|
|
|
|
|
debug!(
|
|
|
|
|
"Proposed change: {:?}\n{}",
|
|
|
|
|
new_center,
|
|
|
|
|
new_positions,
|
|
|
|
|
new_shape.as_ascii()
|
|
|
|
|
);
|
|
|
|
|
match new_center {
|
|
|
|
|
Err(GameError::OutOfBoundsLeft) | Err(GameError::OutOfBoundsRight) => (), // Do nothing
|
|
|
|
|
Err(GameError::OutOfBoundsDown) => {
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
}
|
|
|
|
|
Err(GameError::Collision) => panic!("This shouldn't happen!"),
|
|
|
|
|
Ok(new_center) => {
|
|
|
|
|
let new_blocks = new_shape.coordinates(&new_center);
|
|
|
|
|
for block_gp in new_blocks {
|
|
|
|
|
match block_gp {
|
|
|
|
|
Err(GameError::OutOfBoundsLeft) | Err(GameError::OutOfBoundsRight) => {
|
|
|
|
|
return;
|
|
|
|
|
} // Do nothing
|
|
|
|
|
Err(GameError::OutOfBoundsDown) => {
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Err(GameError::Collision) => panic!("This shouldn't happen!"),
|
|
|
|
|
Ok(gp) => {
|
|
|
|
|
for other_gp in inactive.iter() {
|
|
|
|
|
// If there would be a collision between blocks
|
|
|
|
|
if gp == *other_gp {
|
|
|
|
|
// And we are moving down
|
|
|
|
|
if *trigger.event() == Movement::Down {
|
|
|
|
|
// De-activate this piece
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
for position in new_positions {
|
|
|
|
|
match position {
|
|
|
|
|
Err(GameError::OutOfBoundsLeft) | Err(GameError::OutOfBoundsRight) => (), // Do nothing
|
|
|
|
|
Err(GameError::OutOfBoundsDown) => {
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
}
|
|
|
|
|
Err(GameError::Collision) => panic!("This shouldn't happen!"),
|
|
|
|
|
Ok(new_center) => {
|
|
|
|
|
let new_blocks = new_shape.coordinates(&new_center);
|
|
|
|
|
for block_gp in new_blocks {
|
|
|
|
|
match block_gp {
|
|
|
|
|
Err(GameError::OutOfBoundsLeft) | Err(GameError::OutOfBoundsRight) => {
|
|
|
|
|
return;
|
|
|
|
|
} // Do nothing
|
|
|
|
|
Err(GameError::OutOfBoundsDown) => {
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Err(GameError::Collision) => panic!("This shouldn't happen!"),
|
|
|
|
|
Ok(gp) => {
|
|
|
|
|
for other_gp in inactive.iter() {
|
|
|
|
|
// If there would be a collision between blocks
|
|
|
|
|
if gp == *other_gp {
|
|
|
|
|
// And we are moving down
|
|
|
|
|
if *trigger.event() == Movement::Down {
|
|
|
|
|
// De-activate this piece
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
}
|
|
|
|
|
// Regardless, cancel the move
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Regardless, cancel the move
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
debug!("Checks passed for {position:?}, committing change");
|
|
|
|
|
|
|
|
|
|
// Update center
|
|
|
|
|
let mut gp = grid_positions.get_mut(trigger.target()).unwrap();
|
|
|
|
|
*gp = new_center;
|
|
|
|
|
|
|
|
|
|
// Update shape/rotation
|
|
|
|
|
let mut s = shape.get_mut(trigger.target()).unwrap();
|
|
|
|
|
*s = new_shape;
|
|
|
|
|
}
|
|
|
|
|
// Update center
|
|
|
|
|
let mut gp = grid_positions.get_mut(trigger.target()).unwrap();
|
|
|
|
|
*gp = new_center;
|
|
|
|
|
// Update shape/rotation
|
|
|
|
|
let mut s = shape.get_mut(trigger.target()).unwrap();
|
|
|
|
|
*s = new_shape;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
@ -796,13 +835,15 @@ fn deactivate_shape(
|
|
|
|
|
events.read().for_each(|target| {
|
|
|
|
|
parent.iter_descendants(target).for_each(|block| {
|
|
|
|
|
let GridPosition { y, .. } = grid_positions.get(block).unwrap();
|
|
|
|
|
let parent_line = lines
|
|
|
|
|
if let Some(parent_line) = lines
|
|
|
|
|
.iter()
|
|
|
|
|
.find_map(|(e, Line(i))| (*y == *i).then_some(e))
|
|
|
|
|
.unwrap(); // TODO: This crashed once kinda late in a game... why?
|
|
|
|
|
commands
|
|
|
|
|
.entity(parent_line)
|
|
|
|
|
.add_one_related::<LineBlock>(block);
|
|
|
|
|
.find_map(|(e, Line(i))| (*y == *i).then_some(e)) {
|
|
|
|
|
commands
|
|
|
|
|
.entity(parent_line)
|
|
|
|
|
.add_one_related::<LineBlock>(block);
|
|
|
|
|
} else {
|
|
|
|
|
error!("wtf?");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
commands.entity(target).despawn();
|
|
|
|
|
});
|
|
|
|
|
|