|
|
|
@ -3,7 +3,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
use games::*;
|
|
|
|
use games::*;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Detect when piece is going to go out of bounds and restirct parent from moving there
|
|
|
|
// *TODO: Detect when piece is going to go out of bounds and restirct parent from moving there
|
|
|
|
// TODO: When shape touches the rest of the pieces, re-parent to line entity
|
|
|
|
// TODO: When shape touches the rest of the pieces, re-parent to line entity
|
|
|
|
// TODO: When line is "full" (has 10 children) clear line and add to score
|
|
|
|
// TODO: When line is "full" (has 10 children) clear line and add to score
|
|
|
|
|
|
|
|
|
|
|
|
@ -16,7 +16,7 @@ fn main() {
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.init_state::<Falling>()
|
|
|
|
.init_state::<Falling>()
|
|
|
|
.add_systems(Startup, (init_pieces, init_debug_ui))
|
|
|
|
.add_systems(Startup, (init_world, init_debug_ui))
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
(
|
|
|
|
@ -40,6 +40,7 @@ fn main() {
|
|
|
|
sync_singleton_to_ui::<Orientation>.run_if(any_component_changed::<Orientation>),
|
|
|
|
sync_singleton_to_ui::<Orientation>.run_if(any_component_changed::<Orientation>),
|
|
|
|
check_collision.run_if(any_component_changed::<GridPosition>),
|
|
|
|
check_collision.run_if(any_component_changed::<GridPosition>),
|
|
|
|
add_piece.run_if(not(any_with_component::<Shape>)),
|
|
|
|
add_piece.run_if(not(any_with_component::<Shape>)),
|
|
|
|
|
|
|
|
clear_line.run_if(any_component_changed::<LineBlocks>),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.add_systems(Update, draw_grid)
|
|
|
|
.add_systems(Update, draw_grid)
|
|
|
|
@ -81,17 +82,35 @@ impl Display for Shape {
|
|
|
|
|
|
|
|
|
|
|
|
// The blocks making up this shape
|
|
|
|
// The blocks making up this shape
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component)]
|
|
|
|
#[relationship_target(relationship = BlockOf)]
|
|
|
|
#[relationship_target(relationship = ShapeBlock)]
|
|
|
|
struct ShapeBlocks(Vec<Entity>);
|
|
|
|
struct ShapeBlocks(Vec<Entity>);
|
|
|
|
|
|
|
|
|
|
|
|
/// A part of a piece, i.e., a single square of a piece
|
|
|
|
/// A part of a piece, i.e., a single square of a piece
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
#[relationship(relationship_target = ShapeBlocks)]
|
|
|
|
#[relationship(relationship_target = ShapeBlocks)]
|
|
|
|
struct BlockOf {
|
|
|
|
struct ShapeBlock {
|
|
|
|
#[relationship]
|
|
|
|
#[relationship]
|
|
|
|
parent: Entity,
|
|
|
|
shape: Entity,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The blocks making up this shape
|
|
|
|
|
|
|
|
#[derive(Component, Default)]
|
|
|
|
|
|
|
|
#[relationship_target(relationship = LineBlock)]
|
|
|
|
|
|
|
|
struct LineBlocks(Vec<Entity>);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// A part of a piece, i.e., a single square of a piece
|
|
|
|
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
|
|
|
|
#[require(Transform, Visibility)]
|
|
|
|
|
|
|
|
#[relationship(relationship_target = LineBlocks)]
|
|
|
|
|
|
|
|
struct LineBlock {
|
|
|
|
|
|
|
|
#[relationship]
|
|
|
|
|
|
|
|
line: Entity,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A line holds up to 10 blocks before being cleared
|
|
|
|
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
|
|
|
|
struct Line(u8);
|
|
|
|
|
|
|
|
|
|
|
|
// Just marks a block either of a shape or line
|
|
|
|
// Just marks a block either of a shape or line
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
#[derive(Component, Debug)]
|
|
|
|
struct Block;
|
|
|
|
struct Block;
|
|
|
|
@ -154,7 +173,7 @@ impl GridPosition {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn is_colliding_with(&self, other: &Self) -> bool {
|
|
|
|
fn is_colliding_with(&self, other: &Self) -> bool {
|
|
|
|
self.x == other.x && self.y - 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) -> Self {
|
|
|
|
@ -263,7 +282,7 @@ struct Visuals {
|
|
|
|
mesh: Handle<Mesh>,
|
|
|
|
mesh: Handle<Mesh>,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn init_pieces(
|
|
|
|
fn init_world(
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
|
|
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
|
|
|
@ -275,6 +294,10 @@ fn init_pieces(
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
mesh: meshes.add(Rectangle::new(SCALE, SCALE)),
|
|
|
|
mesh: meshes.add(Rectangle::new(SCALE, SCALE)),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(0..20).for_each(|i| {
|
|
|
|
|
|
|
|
commands.spawn((Line(i), LineBlocks::default()));
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn init_debug_ui(mut commands: Commands) {
|
|
|
|
fn init_debug_ui(mut commands: Commands) {
|
|
|
|
@ -442,7 +465,7 @@ fn set_piece(
|
|
|
|
Changed<Orientation>,
|
|
|
|
Changed<Orientation>,
|
|
|
|
)>,
|
|
|
|
)>,
|
|
|
|
>,
|
|
|
|
>,
|
|
|
|
mut blocks: Query<&mut RelativePosition, With<BlockOf>>,
|
|
|
|
mut blocks: Query<&mut RelativePosition, With<ShapeBlock>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
visuals: Res<Visuals>,
|
|
|
|
visuals: Res<Visuals>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
@ -456,7 +479,7 @@ fn set_piece(
|
|
|
|
if blocks.is_empty() {
|
|
|
|
if blocks.is_empty() {
|
|
|
|
commands
|
|
|
|
commands
|
|
|
|
.entity(e)
|
|
|
|
.entity(e)
|
|
|
|
.with_related_entities::<BlockOf>(|parent| {
|
|
|
|
.with_related_entities::<ShapeBlock>(|parent| {
|
|
|
|
positions.into_iter().for_each(|rp| {
|
|
|
|
positions.into_iter().for_each(|rp| {
|
|
|
|
parent.spawn((Mesh2d(mesh.clone()), MeshMaterial2d(mat.clone()), rp, Block));
|
|
|
|
parent.spawn((Mesh2d(mesh.clone()), MeshMaterial2d(mat.clone()), rp, Block));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@ -472,6 +495,7 @@ fn set_piece(
|
|
|
|
|
|
|
|
|
|
|
|
fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed<GridPosition>>) {
|
|
|
|
fn update_position(mut query: Query<(&GridPosition, &mut Transform), Changed<GridPosition>>) {
|
|
|
|
query.iter_mut().for_each(|(gp, mut t)| {
|
|
|
|
query.iter_mut().for_each(|(gp, mut t)| {
|
|
|
|
|
|
|
|
debug_assert!(gp.x < X_MAX, "block x > x_max");
|
|
|
|
t.translation = gp.into();
|
|
|
|
t.translation = gp.into();
|
|
|
|
debug!("Updating position {:?}", t.translation);
|
|
|
|
debug!("Updating position {:?}", t.translation);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@ -548,7 +572,7 @@ fn shape_block_movement(
|
|
|
|
Without<RelativePosition>,
|
|
|
|
Without<RelativePosition>,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
>,
|
|
|
|
>,
|
|
|
|
mut blocks: Query<(&mut GridPosition, &RelativePosition), With<BlockOf>>,
|
|
|
|
mut blocks: Query<(&mut GridPosition, &RelativePosition), With<ShapeBlock>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
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)| {
|
|
|
|
@ -574,8 +598,9 @@ fn clock_cycle(n: f32) -> impl FnMut(Res<Time>, Local<f32>) -> bool {
|
|
|
|
/// Check if the active piece is about to collide with the ground
|
|
|
|
/// Check if the active piece is about to collide with the ground
|
|
|
|
fn check_collision(
|
|
|
|
fn check_collision(
|
|
|
|
active: Query<(&GridPosition, Entity), (With<Block>, With<RelativePosition>)>,
|
|
|
|
active: Query<(&GridPosition, Entity), (With<Block>, With<RelativePosition>)>,
|
|
|
|
shape: Single<(Entity, &GridPosition), With<ShapeBlocks>>,
|
|
|
|
shape: Single<(&GridPosition, Entity), With<ShapeBlocks>>,
|
|
|
|
inactive: Query<&GridPosition, (With<Block>, Without<RelativePosition>)>,
|
|
|
|
inactive: Query<&GridPosition, (With<Block>, Without<RelativePosition>)>,
|
|
|
|
|
|
|
|
lines: Query<(&Line, Entity)>,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
// TODO: Easy optimizations
|
|
|
|
// TODO: Easy optimizations
|
|
|
|
@ -597,17 +622,25 @@ fn check_collision(
|
|
|
|
|
|
|
|
|
|
|
|
let blocks: Vec<Entity> = active
|
|
|
|
let blocks: Vec<Entity> = active
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.map(|(_, e)| commands.entity(e).remove::<RelativePosition>().id())
|
|
|
|
.map(|(gp, e)| commands.entity(e).remove::<RelativePosition>().id())
|
|
|
|
.collect();
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
debug_assert_eq!(blocks.len(), 4, "Shapes should have 4 blocks");
|
|
|
|
debug_assert_eq!(blocks.len(), 4, "Shapes should have 4 blocks");
|
|
|
|
|
|
|
|
|
|
|
|
info!("De-relating blocks {:?} and shape {:?}", blocks, *shape);
|
|
|
|
info!("De-relating blocks {:?} and shape {:?}", blocks, *shape);
|
|
|
|
let (shape_e, _shape_pos) = *shape;
|
|
|
|
let (_shape_pos, shape_e) = *shape;
|
|
|
|
commands
|
|
|
|
commands
|
|
|
|
.entity(shape_e)
|
|
|
|
.entity(shape_e)
|
|
|
|
.remove_related::<BlockOf>(blocks.as_slice())
|
|
|
|
.remove_related::<ShapeBlock>(blocks.as_slice())
|
|
|
|
.despawn();
|
|
|
|
.despawn();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
active.iter().for_each(|(GridPosition { y, .. }, block_e)| {
|
|
|
|
|
|
|
|
lines.iter().for_each(|(Line(i), line_e)| {
|
|
|
|
|
|
|
|
if *y == *i as u32 {
|
|
|
|
|
|
|
|
commands.entity(line_e).add_one_related::<LineBlock>(block_e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -615,3 +648,18 @@ 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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// When a line reaches 10 blocks, clear it
|
|
|
|
|
|
|
|
fn clear_line(
|
|
|
|
|
|
|
|
lines: Query<(Entity, &LineBlocks), (With<LineBlocks>, Changed<LineBlocks>)>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
lines.iter().for_each(|(e, lb)| {
|
|
|
|
|
|
|
|
if lb.0.len() == 10 {
|
|
|
|
|
|
|
|
commands.entity(e).despawn_related::<LineBlocks>();
|
|
|
|
|
|
|
|
// TODO: re-parent all blocks above this to the next line down
|
|
|
|
|
|
|
|
// TODO: Parent blocks to lines for movement
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|