|
|
|
@ -115,13 +115,43 @@ struct Line(u8);
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
struct Block;
|
|
|
|
struct Block;
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
#[derive(Component, Event, Debug)]
|
|
|
|
#[require(GridPosition)]
|
|
|
|
#[require(GridPosition)]
|
|
|
|
struct RelativePosition {
|
|
|
|
struct RelativePosition {
|
|
|
|
x: i8,
|
|
|
|
x: i8,
|
|
|
|
y: 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 {
|
|
|
|
impl From<(i8, i8)> for RelativePosition {
|
|
|
|
fn from((x, y): (i8, i8)) -> RelativePosition {
|
|
|
|
fn from((x, y): (i8, i8)) -> RelativePosition {
|
|
|
|
RelativePosition { x, y }
|
|
|
|
RelativePosition { x, y }
|
|
|
|
@ -176,15 +206,29 @@ impl GridPosition {
|
|
|
|
self.x == other.x && self.y.saturating_sub(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 {
|
|
|
|
fn add_offset(&self, RelativePosition { x: x1, y: y1 }: &RelativePosition) -> Result<Self, GameError> {
|
|
|
|
debug!("{} + {}, {} + {}", self.x, *x1 as i32, self.y, *y1 as i32);
|
|
|
|
let x = self.x.checked_add_signed(*x1 as i32).ok_or(GameError::OutOfBoundsLeft)?;
|
|
|
|
GridPosition {
|
|
|
|
let y = self.y.checked_add_signed(*y1 as i32).ok_or(GameError::OutOfBoundsDown)?;
|
|
|
|
x: self.x.checked_add_signed(*x1 as i32).unwrap(),
|
|
|
|
if x >= X_MAX { // TODO: y > Y_MAX?
|
|
|
|
y: self.y.checked_add_signed(*y1 as i32).unwrap(),
|
|
|
|
Err(GameError::OutOfBoundsRight)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
Ok(GridPosition { x, y })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Error, Debug)]
|
|
|
|
|
|
|
|
enum GameError {
|
|
|
|
|
|
|
|
#[error("Coordinates are out of bounds: Left")]
|
|
|
|
|
|
|
|
OutOfBoundsLeft,
|
|
|
|
|
|
|
|
#[error("Coordinates are out of bounds: Right")]
|
|
|
|
|
|
|
|
OutOfBoundsRight,
|
|
|
|
|
|
|
|
#[error("Coordinates are out of bounds: Down")]
|
|
|
|
|
|
|
|
OutOfBoundsDown,
|
|
|
|
|
|
|
|
#[error("Coordiante collision")]
|
|
|
|
|
|
|
|
Collision,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Default for GridPosition {
|
|
|
|
impl Default for GridPosition {
|
|
|
|
fn default() -> Self {
|
|
|
|
fn default() -> Self {
|
|
|
|
GridPosition { x: 5, y: Y_MAX }
|
|
|
|
GridPosition { x: 5, y: Y_MAX }
|
|
|
|
@ -229,7 +273,7 @@ impl std::ops::AddAssign<&GridPosition> for GridPosition {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component, Default, Debug)]
|
|
|
|
#[derive(Component, Default, Event, Clone, Debug)]
|
|
|
|
enum Orientation {
|
|
|
|
enum Orientation {
|
|
|
|
#[default]
|
|
|
|
#[default]
|
|
|
|
Up,
|
|
|
|
Up,
|
|
|
|
@ -503,9 +547,10 @@ fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed<Gri
|
|
|
|
|
|
|
|
|
|
|
|
fn kb_input(
|
|
|
|
fn kb_input(
|
|
|
|
mut events: EventReader<KeyboardInput>,
|
|
|
|
mut events: EventReader<KeyboardInput>,
|
|
|
|
mut query: Query<(&mut GridPosition, &mut Orientation, &mut Shape)>,
|
|
|
|
mut query: Query<(Entity, &Orientation, &mut Shape)>,
|
|
|
|
curr: Res<State<Falling>>,
|
|
|
|
curr: Res<State<Falling>>,
|
|
|
|
mut next: ResMut<NextState<Falling>>,
|
|
|
|
mut next: ResMut<NextState<Falling>>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.read().for_each(
|
|
|
|
events.read().for_each(
|
|
|
|
|KeyboardInput {
|
|
|
|
|KeyboardInput {
|
|
|
|
@ -514,14 +559,22 @@ fn kb_input(
|
|
|
|
if let ButtonState::Pressed = state {
|
|
|
|
if let ButtonState::Pressed = state {
|
|
|
|
// TODO: Restict movement based on size/orientation of piece
|
|
|
|
// TODO: Restict movement based on size/orientation of piece
|
|
|
|
// Check if children would be outside play area...
|
|
|
|
// Check if children would be outside play area...
|
|
|
|
query.iter_mut().for_each(|(mut gp, mut o, mut s)| {
|
|
|
|
query.iter_mut().for_each(|(e, o, mut s)| {
|
|
|
|
match key_code {
|
|
|
|
match key_code {
|
|
|
|
// Up arrow should rotate if in falling mode
|
|
|
|
// Up arrow should rotate if in falling mode
|
|
|
|
// Only move up if in falling::off mode
|
|
|
|
// Only move up if in falling::off mode
|
|
|
|
KeyCode::ArrowUp => *o = o.prev(),
|
|
|
|
KeyCode::ArrowUp => {
|
|
|
|
KeyCode::ArrowDown => *gp = gp.move_down(),
|
|
|
|
commands.entity(e).trigger(o.prev());
|
|
|
|
KeyCode::ArrowLeft => *gp = gp.move_left(),
|
|
|
|
}
|
|
|
|
KeyCode::ArrowRight => *gp = gp.move_right(),
|
|
|
|
KeyCode::ArrowDown => {
|
|
|
|
|
|
|
|
commands.entity(e).trigger(RelativePosition::down());
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
KeyCode::ArrowLeft => {
|
|
|
|
|
|
|
|
commands.entity(e).trigger(RelativePosition::left());
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
KeyCode::ArrowRight => {
|
|
|
|
|
|
|
|
commands.entity(e).trigger(RelativePosition::right());
|
|
|
|
|
|
|
|
},
|
|
|
|
KeyCode::Space => next.set(match curr.get() {
|
|
|
|
KeyCode::Space => next.set(match curr.get() {
|
|
|
|
Falling::On => Falling::Off,
|
|
|
|
Falling::On => Falling::Off,
|
|
|
|
Falling::Off => Falling::On,
|
|
|
|
Falling::Off => Falling::On,
|
|
|
|
@ -552,14 +605,9 @@ fn draw_grid(mut gizmos: Gizmos) {
|
|
|
|
.outer_edges();
|
|
|
|
.outer_edges();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn falling(mut shape: Query<&mut GridPosition, With<Shape>>) {
|
|
|
|
fn falling(mut shape: Query<Entity, With<Shape>>, mut commands: Commands) {
|
|
|
|
shape.iter_mut().for_each(|mut gp| {
|
|
|
|
shape.iter_mut().for_each(|e| {
|
|
|
|
let next = gp.move_down();
|
|
|
|
commands.entity(e).trigger(RelativePosition::down());
|
|
|
|
if next != *gp {
|
|
|
|
|
|
|
|
*gp = next;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// TODO: What to do here??
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -576,7 +624,7 @@ fn shape_block_movement(
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
shape.iter().for_each(|shape_gp| {
|
|
|
|
shape.iter().for_each(|shape_gp| {
|
|
|
|
blocks.iter_mut().for_each(|(mut block_gp, block_rp)| {
|
|
|
|
blocks.iter_mut().for_each(|(mut block_gp, block_rp)| {
|
|
|
|
*block_gp = shape_gp.add_offset(block_rp);
|
|
|
|
*block_gp = shape_gp.add_offset(block_rp).expect("Cannot move there...");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -646,7 +694,7 @@ fn check_collision(
|
|
|
|
|
|
|
|
|
|
|
|
fn add_piece(mut commands: Commands) {
|
|
|
|
fn add_piece(mut commands: Commands) {
|
|
|
|
// TODO: Choose a different piece
|
|
|
|
// TODO: Choose a different piece
|
|
|
|
commands.spawn((Orientation::default(), GridPosition::default(), Shape::T));
|
|
|
|
commands.spawn((Orientation::default(), GridPosition::default(), Shape::T)).observe(movement).observe(rotation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// When a line reaches 10 blocks, clear it
|
|
|
|
/// When a line reaches 10 blocks, clear it
|
|
|
|
@ -659,7 +707,57 @@ fn clear_line(
|
|
|
|
commands.entity(e).despawn_related::<LineBlocks>();
|
|
|
|
commands.entity(e).despawn_related::<LineBlocks>();
|
|
|
|
// TODO: re-parent all blocks above this to the next line down
|
|
|
|
// TODO: re-parent all blocks above this to the next line down
|
|
|
|
// TODO: Parent blocks to lines for movement
|
|
|
|
// TODO: Parent blocks to lines for movement
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn movement(
|
|
|
|
|
|
|
|
trigger: Trigger<RelativePosition>,
|
|
|
|
|
|
|
|
mut shapes: Query<&mut GridPosition, With<Shape>>,
|
|
|
|
|
|
|
|
mut active: Query<&mut GridPosition, (With<RelativePosition>, Without<Shape>)>,
|
|
|
|
|
|
|
|
inactive: Query<&GridPosition, (Without<RelativePosition>, Without<Shape>)>,
|
|
|
|
|
|
|
|
blocks: Query<&ShapeBlocks>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Find any errors
|
|
|
|
|
|
|
|
for block in blocks.iter_descendants(trigger.target()) {
|
|
|
|
|
|
|
|
// Get the child's grid position
|
|
|
|
|
|
|
|
let gp = active.get(block).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match gp.add_offset(trigger.event()) {
|
|
|
|
|
|
|
|
// If this would go out of bounds to the left or right, prevent this move
|
|
|
|
|
|
|
|
Err(GameError::OutOfBoundsLeft) | Err(GameError::OutOfBoundsRight) => {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this would go out of bounds down, or cause a collision, remove the active
|
|
|
|
|
|
|
|
// component from the piece
|
|
|
|
|
|
|
|
Err(GameError::OutOfBoundsDown) | Err(GameError::Collision) => {
|
|
|
|
|
|
|
|
// remove active component
|
|
|
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(new_gp) => {
|
|
|
|
|
|
|
|
// If any children collide with the new position, remove the active component
|
|
|
|
|
|
|
|
if inactive.iter().any(|other_gp| new_gp == *other_gp) {
|
|
|
|
|
|
|
|
commands.entity(trigger.target()).remove::<Shape>();
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the above checks went ok, we can move
|
|
|
|
|
|
|
|
let mut gp = shapes.get_mut(trigger.target()).unwrap();
|
|
|
|
|
|
|
|
*gp = gp.add_offset(trigger.event()).unwrap();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn rotation(
|
|
|
|
|
|
|
|
trigger: Trigger<Orientation>,
|
|
|
|
|
|
|
|
mut q: Query<(&mut Orientation, &mut GridPosition)>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
let (mut o, mut _gp) = q.get_mut(trigger.target()).unwrap();
|
|
|
|
|
|
|
|
// If children would go out of bounds to the left
|
|
|
|
|
|
|
|
// If children would not go out of bounds to the right
|
|
|
|
|
|
|
|
// If children would not go out of bounds to the down
|
|
|
|
|
|
|
|
*o = trigger.event().clone();
|
|
|
|
|
|
|
|
}
|
|
|
|
|