|
|
|
@ -1,12 +1,9 @@
|
|
|
|
|
|
|
|
#![allow(clippy::type_complexity)]
|
|
|
|
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO:
|
|
|
|
// TODO:
|
|
|
|
// - When shape asset is updated, shape layout update in real time
|
|
|
|
// - When shape asset is updated, shape layout update in real time
|
|
|
|
// - Rotating shape when arrow up pressed
|
|
|
|
|
|
|
|
// - Shape moves left/right/down when respective keys pressed
|
|
|
|
|
|
|
|
// - Add Orientation component
|
|
|
|
|
|
|
|
// - Add functionality to ShapeLayout
|
|
|
|
|
|
|
|
// - Write tests before functionality
|
|
|
|
|
|
|
|
// - Debug: gizmo outline entities
|
|
|
|
// - Debug: gizmo outline entities
|
|
|
|
// - conditional enable/disable
|
|
|
|
// - conditional enable/disable
|
|
|
|
// - entities without mesh should draw empty cross
|
|
|
|
// - entities without mesh should draw empty cross
|
|
|
|
@ -33,15 +30,25 @@ impl Plugin for BlocksPlugin {
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
app.init_asset::<ShapeAsset>()
|
|
|
|
app.init_asset::<ShapeAsset>()
|
|
|
|
.init_asset_loader::<ShapeAssetLoader>()
|
|
|
|
.init_asset_loader::<ShapeAssetLoader>()
|
|
|
|
|
|
|
|
.add_message::<Movement>()
|
|
|
|
.add_systems(OnEnter(Loading(true)), load_assets.run_if(run_once))
|
|
|
|
.add_systems(OnEnter(Loading(true)), load_assets.run_if(run_once))
|
|
|
|
.add_systems(OnEnter(GameState::Setup), (setup_camera, setup_blocks))
|
|
|
|
.add_systems(OnEnter(GameState::Setup), (setup_camera, setup_blocks))
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
updated_shape_asset.run_if(on_message::<AssetEvent<ShapeAsset>>),
|
|
|
|
(
|
|
|
|
|
|
|
|
updated_shape_asset.run_if(on_message::<AssetEvent<ShapeAsset>>),
|
|
|
|
|
|
|
|
update_grid_position_transform.run_if(
|
|
|
|
|
|
|
|
any_component_added::<GridPosition>
|
|
|
|
|
|
|
|
.or(any_component_changed::<GridPosition>),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
propogate_orientation.run_if(any_component_changed::<Orientation>),
|
|
|
|
|
|
|
|
propogate_grid_position.run_if(any_component_changed::<GridPosition>),
|
|
|
|
|
|
|
|
handle_kb_input.run_if(on_message::<KeyboardInput>),
|
|
|
|
|
|
|
|
handle_movement.run_if(on_message::<Movement>),
|
|
|
|
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.add_observer(add_shape)
|
|
|
|
.add_observer(add_shape)
|
|
|
|
.add_observer(add_relative_position)
|
|
|
|
.add_observer(add_relative_position);
|
|
|
|
.add_observer(add_grid_position);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -53,7 +60,7 @@ const Y_MAX: usize = 20;
|
|
|
|
/// Stores shape data in an asset file, likely toml
|
|
|
|
/// Stores shape data in an asset file, likely toml
|
|
|
|
#[derive(Asset, TypePath, Debug, Deserialize)]
|
|
|
|
#[derive(Asset, TypePath, Debug, Deserialize)]
|
|
|
|
struct ShapeAsset {
|
|
|
|
struct ShapeAsset {
|
|
|
|
layout: ShapeLayout,
|
|
|
|
layout: Vec<Vec<u8>>,
|
|
|
|
#[serde(default = "default_tint")]
|
|
|
|
#[serde(default = "default_tint")]
|
|
|
|
tint: String,
|
|
|
|
tint: String,
|
|
|
|
#[serde(skip)]
|
|
|
|
#[serde(skip)]
|
|
|
|
@ -69,22 +76,25 @@ fn default_tint() -> String {
|
|
|
|
impl ShapeAsset {
|
|
|
|
impl ShapeAsset {
|
|
|
|
fn as_bundle(&self) -> impl Bundle {
|
|
|
|
fn as_bundle(&self) -> impl Bundle {
|
|
|
|
// Get shape block's relative positions
|
|
|
|
// Get shape block's relative positions
|
|
|
|
let [a, b, c, d] = self.layout.positions();
|
|
|
|
let layout = ShapeLayout::new(self.layout.clone());
|
|
|
|
|
|
|
|
let [a, b, c, d] = layout.positions(Orientation::Up);
|
|
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
(
|
|
|
|
GridPosition { x: 5, y: 5 }, // TEMP
|
|
|
|
GridPosition { x: 5, y: 5 }, // TEMP
|
|
|
|
|
|
|
|
layout.clone(),
|
|
|
|
|
|
|
|
Orientation::Up,
|
|
|
|
related!(ShapeBlocks[
|
|
|
|
related!(ShapeBlocks[
|
|
|
|
(self.mesh.clone(), self.material.clone(), a),
|
|
|
|
(self.mesh.clone(), self.material.clone(), a),
|
|
|
|
(self.mesh.clone(), self.material.clone(), b),
|
|
|
|
(self.mesh.clone(), self.material.clone(), b),
|
|
|
|
(self.mesh.clone(), self.material.clone(), c),
|
|
|
|
(self.mesh.clone(), self.material.clone(), c),
|
|
|
|
(self.mesh.clone(), self.material.clone(), d),
|
|
|
|
(self.mesh.clone(), self.material.clone(), d),
|
|
|
|
])
|
|
|
|
]),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The main play area grid coordinates
|
|
|
|
/// The main play area grid coordinates
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component, Debug, Clone)]
|
|
|
|
#[require(Transform)]
|
|
|
|
#[require(Transform)]
|
|
|
|
struct GridPosition {
|
|
|
|
struct GridPosition {
|
|
|
|
pub x: usize,
|
|
|
|
pub x: usize,
|
|
|
|
@ -108,6 +118,31 @@ impl Into<Vec3> for &GridPosition {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl std::ops::AddAssign<RelativePosition> for GridPosition {
|
|
|
|
|
|
|
|
fn add_assign(&mut self, rhs: RelativePosition) {
|
|
|
|
|
|
|
|
self.x = self.x.strict_add_signed(rhs.x);
|
|
|
|
|
|
|
|
self.y = self.y.strict_add_signed(rhs.y);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl std::ops::SubAssign<RelativePosition> for GridPosition {
|
|
|
|
|
|
|
|
fn sub_assign(&mut self, rhs: RelativePosition) {
|
|
|
|
|
|
|
|
self.x = self.x.strict_sub_signed(rhs.x);
|
|
|
|
|
|
|
|
self.y = self.y.strict_sub_signed(rhs.y);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl std::ops::Add<RelativePosition> for GridPosition {
|
|
|
|
|
|
|
|
type Output = GridPosition;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn add(self, rhs: RelativePosition) -> GridPosition {
|
|
|
|
|
|
|
|
GridPosition {
|
|
|
|
|
|
|
|
x: self.x.strict_add_signed(rhs.x),
|
|
|
|
|
|
|
|
y: self.y.strict_add_signed(rhs.y),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Block positions relative to the shape's center
|
|
|
|
/// Block positions relative to the shape's center
|
|
|
|
#[derive(Component, PartialEq, Debug, Clone, Copy)]
|
|
|
|
#[derive(Component, PartialEq, Debug, Clone, Copy)]
|
|
|
|
pub(crate) struct RelativePosition {
|
|
|
|
pub(crate) struct RelativePosition {
|
|
|
|
@ -115,37 +150,72 @@ pub(crate) struct RelativePosition {
|
|
|
|
pub y: isize,
|
|
|
|
pub y: isize,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl RelativePosition {
|
|
|
|
|
|
|
|
fn new(x: isize, y: isize) -> Self {
|
|
|
|
|
|
|
|
RelativePosition { x, y }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl From<(isize, isize)> for RelativePosition {
|
|
|
|
impl From<(isize, isize)> for RelativePosition {
|
|
|
|
fn from((x, y): (isize, isize)) -> Self {
|
|
|
|
fn from((x, y): (isize, isize)) -> Self {
|
|
|
|
RelativePosition { x, y }
|
|
|
|
RelativePosition::new(x, y)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Layout for a given shape
|
|
|
|
/// Layout for a given shape
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[derive(Debug, Component, Deserialize, PartialEq, Clone)]
|
|
|
|
pub(crate) struct ShapeLayout(pub Vec<Vec<u8>>);
|
|
|
|
pub(crate) struct ShapeLayout {
|
|
|
|
|
|
|
|
pub matrix: Vec<Vec<u8>>,
|
|
|
|
|
|
|
|
#[serde(skip)]
|
|
|
|
|
|
|
|
symmetrical: bool,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl ShapeLayout {
|
|
|
|
impl ShapeLayout {
|
|
|
|
pub(crate) fn positions(&self) -> [RelativePosition; 4] {
|
|
|
|
pub(crate) fn new(matrix: Vec<Vec<u8>>) -> Self {
|
|
|
|
|
|
|
|
let mut s = Self {
|
|
|
|
|
|
|
|
matrix,
|
|
|
|
|
|
|
|
symmetrical: true,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if symmetrical
|
|
|
|
|
|
|
|
s.symmetrical = s.matrix == s.rotated_matrix(Orientation::Down);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) fn positions(&self, o: Orientation) -> [RelativePosition; 4] {
|
|
|
|
let mut c: Vec<RelativePosition> = Vec::with_capacity(4);
|
|
|
|
let mut c: Vec<RelativePosition> = Vec::with_capacity(4);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let layout = self.rotated_matrix(o);
|
|
|
|
|
|
|
|
|
|
|
|
let center: (isize, isize) = {
|
|
|
|
let center: (isize, isize) = {
|
|
|
|
// This could be less lines of code but it would be confusing af
|
|
|
|
// This could be less lines of code but it would be confusing af
|
|
|
|
let y = if self.0.len() % 2 == 1 {
|
|
|
|
let y = if layout.len() % 2 == 1 {
|
|
|
|
self.0.len() / 2
|
|
|
|
layout.len() / 2
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
self.0.len() / 2 - 1
|
|
|
|
layout.len() / 2 - 1
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let x = if self.0[0].len() % 2 == 1 {
|
|
|
|
let x = if layout[0].len() % 2 == 1 {
|
|
|
|
self.0[0].len() / 2
|
|
|
|
layout[0].len() / 2
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
self.0[0].len() / 2 - 1
|
|
|
|
layout[0].len() / 2 - 1
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let (x_off, y_off) = match self.symmetrical {
|
|
|
|
|
|
|
|
true => (0, 0),
|
|
|
|
|
|
|
|
false => match o {
|
|
|
|
|
|
|
|
Orientation::Up => (0, 0),
|
|
|
|
|
|
|
|
Orientation::Right => (0, 0),
|
|
|
|
|
|
|
|
Orientation::Down => (0, 1),
|
|
|
|
|
|
|
|
Orientation::Left => (1, 0),
|
|
|
|
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
(x as isize, y as isize)
|
|
|
|
(x_off + x as isize, y_off + y as isize)
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (y, nested) in self.0.iter().rev().enumerate() {
|
|
|
|
for (y, nested) in layout.iter().rev().enumerate() {
|
|
|
|
for (x, val) in nested.iter().enumerate() {
|
|
|
|
for (x, val) in nested.iter().enumerate() {
|
|
|
|
if *val == 1 {
|
|
|
|
if *val == 1 {
|
|
|
|
c.push(RelativePosition {
|
|
|
|
c.push(RelativePosition {
|
|
|
|
@ -158,6 +228,56 @@ impl ShapeLayout {
|
|
|
|
|
|
|
|
|
|
|
|
[c[0], c[1], c[2], c[3]]
|
|
|
|
[c[0], c[1], c[2], c[3]]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub(crate) fn rotated_matrix(&self, o: Orientation) -> Vec<Vec<u8>> {
|
|
|
|
|
|
|
|
// Now we have the base layout, rotate it based on orientation
|
|
|
|
|
|
|
|
match o {
|
|
|
|
|
|
|
|
Orientation::Up => {
|
|
|
|
|
|
|
|
// The identity
|
|
|
|
|
|
|
|
self.matrix.clone()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Orientation::Right => {
|
|
|
|
|
|
|
|
// The main algorithm
|
|
|
|
|
|
|
|
let cols = self.matrix[0].len();
|
|
|
|
|
|
|
|
let rows = self.matrix.len();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut rotated = vec![vec![0; rows]; cols];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// (i, j) -> (j,rows-1-i)
|
|
|
|
|
|
|
|
for i in 0..rows {
|
|
|
|
|
|
|
|
for j in 0..cols {
|
|
|
|
|
|
|
|
rotated[j][rows - 1 - i] = self.matrix[i][j];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rotated
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Orientation::Down => {
|
|
|
|
|
|
|
|
// HACK: Implemented as repeated turns right
|
|
|
|
|
|
|
|
// TODO: Just hard-code this
|
|
|
|
|
|
|
|
ShapeLayout {
|
|
|
|
|
|
|
|
matrix: self.rotated_matrix(Orientation::Right),
|
|
|
|
|
|
|
|
symmetrical: false
|
|
|
|
|
|
|
|
}.rotated_matrix(Orientation::Right)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Orientation::Left => {
|
|
|
|
|
|
|
|
// The main algorithm
|
|
|
|
|
|
|
|
let cols = self.matrix[0].len();
|
|
|
|
|
|
|
|
let rows = self.matrix.len();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut rotated = vec![vec![0; rows]; cols];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// (i, j) -> (cols-1-j,i)
|
|
|
|
|
|
|
|
for i in 0..rows {
|
|
|
|
|
|
|
|
for j in 0..cols {
|
|
|
|
|
|
|
|
rotated[cols - 1 - j][i] = self.matrix[i][j];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rotated
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
#[derive(Default)]
|
|
|
|
@ -294,13 +414,139 @@ fn add_relative_position(
|
|
|
|
let parent_shape = blocks.get(event.entity).unwrap();
|
|
|
|
let parent_shape = blocks.get(event.entity).unwrap();
|
|
|
|
let rp = relative_positions.get(event.entity).unwrap();
|
|
|
|
let rp = relative_positions.get(event.entity).unwrap();
|
|
|
|
let gp = grid_positions.get(parent_shape.0).unwrap();
|
|
|
|
let gp = grid_positions.get(parent_shape.0).unwrap();
|
|
|
|
commands.entity(event.entity).insert(gp.with_relative_offset(rp));
|
|
|
|
commands
|
|
|
|
|
|
|
|
.entity(event.entity)
|
|
|
|
|
|
|
|
.insert(gp.with_relative_offset(rp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn add_grid_position(
|
|
|
|
/// Populate Transform when GridPosition is added to an entity
|
|
|
|
event: On<Add, GridPosition>,
|
|
|
|
fn update_grid_position_transform(
|
|
|
|
mut query: Query<(&GridPosition, &mut Transform)>,
|
|
|
|
mut query: Query<
|
|
|
|
|
|
|
|
(&GridPosition, &mut Transform),
|
|
|
|
|
|
|
|
Or<(Added<GridPosition>, Changed<GridPosition>)>,
|
|
|
|
|
|
|
|
>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
let (gp, mut t) = query.get_mut(event.entity).unwrap();
|
|
|
|
query.iter_mut().for_each(|(gp, mut t)| {
|
|
|
|
t.translation = gp.into();
|
|
|
|
t.translation = gp.into();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// When grid position is updated on a shape, apply that to blocks
|
|
|
|
|
|
|
|
fn propogate_grid_position(
|
|
|
|
|
|
|
|
parent: Query<(&GridPosition, &ShapeBlocks), Changed<GridPosition>>,
|
|
|
|
|
|
|
|
mut children: Query<(&mut GridPosition, &RelativePosition), Without<ShapeBlocks>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
parent.iter().for_each(|(parent_gp, sbs)| {
|
|
|
|
|
|
|
|
sbs.iter().for_each(|e| {
|
|
|
|
|
|
|
|
let (mut gp, rp) = children.get_mut(e).unwrap();
|
|
|
|
|
|
|
|
*gp = parent_gp.clone() + *rp;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// When a shape's orientation changes, the blocks need to move
|
|
|
|
|
|
|
|
fn propogate_orientation(
|
|
|
|
|
|
|
|
parent: Query<(&GridPosition, &Orientation, &ShapeLayout, &ShapeBlocks), Changed<Orientation>>,
|
|
|
|
|
|
|
|
mut children: Query<&mut GridPosition, Without<ShapeBlocks>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
parent.iter().for_each(|(parent_gp, parent_o, sl, sbs)| {
|
|
|
|
|
|
|
|
let new_layout = sl.positions(*parent_o);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sbs.iter().zip(new_layout).for_each(|(e, rp)| {
|
|
|
|
|
|
|
|
let mut gp = children.get_mut(e).unwrap();
|
|
|
|
|
|
|
|
*gp = parent_gp.clone() + rp;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Movement message used to propose/plan a move
|
|
|
|
|
|
|
|
#[derive(Debug, Message)]
|
|
|
|
|
|
|
|
enum Movement {
|
|
|
|
|
|
|
|
Rotate,
|
|
|
|
|
|
|
|
Left,
|
|
|
|
|
|
|
|
Down,
|
|
|
|
|
|
|
|
Right,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Handle KeyBoard input
|
|
|
|
|
|
|
|
/// Nothing is handled directly in this method,
|
|
|
|
|
|
|
|
/// All key presses result in either a message, event, or state change
|
|
|
|
|
|
|
|
fn handle_kb_input(mut input: MessageReader<KeyboardInput>, mut moves: MessageWriter<Movement>) {
|
|
|
|
|
|
|
|
input.read().for_each(
|
|
|
|
|
|
|
|
|KeyboardInput {
|
|
|
|
|
|
|
|
key_code, state, ..
|
|
|
|
|
|
|
|
}| {
|
|
|
|
|
|
|
|
if *state == ButtonState::Pressed {
|
|
|
|
|
|
|
|
match key_code {
|
|
|
|
|
|
|
|
KeyCode::ArrowLeft => {
|
|
|
|
|
|
|
|
moves.write(Movement::Left);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyCode::ArrowRight => {
|
|
|
|
|
|
|
|
moves.write(Movement::Right);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyCode::ArrowDown => {
|
|
|
|
|
|
|
|
moves.write(Movement::Down);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyCode::ArrowUp => {
|
|
|
|
|
|
|
|
moves.write(Movement::Rotate);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyCode::Escape => todo!("Pause Toggle"),
|
|
|
|
|
|
|
|
_ => (), // Do nothing
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component, Default, Debug, Clone, Copy)]
|
|
|
|
|
|
|
|
pub(crate) enum Orientation {
|
|
|
|
|
|
|
|
#[default]
|
|
|
|
|
|
|
|
Up,
|
|
|
|
|
|
|
|
Right,
|
|
|
|
|
|
|
|
Down,
|
|
|
|
|
|
|
|
Left,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Orientation {
|
|
|
|
|
|
|
|
fn rotated(&self) -> Orientation {
|
|
|
|
|
|
|
|
match self {
|
|
|
|
|
|
|
|
Orientation::Up => Orientation::Right,
|
|
|
|
|
|
|
|
Orientation::Right => Orientation::Down,
|
|
|
|
|
|
|
|
Orientation::Down => Orientation::Left,
|
|
|
|
|
|
|
|
Orientation::Left => Orientation::Up,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn handle_movement(
|
|
|
|
|
|
|
|
mut moves: MessageReader<Movement>,
|
|
|
|
|
|
|
|
mut grid_positions: Query<&mut GridPosition, With<ShapeBlocks>>,
|
|
|
|
|
|
|
|
mut orientations: Query<&mut Orientation, With<ShapeBlocks>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
moves.read().for_each(|m| match m {
|
|
|
|
|
|
|
|
Movement::Left => {
|
|
|
|
|
|
|
|
grid_positions.iter_mut().for_each(|mut gp| {
|
|
|
|
|
|
|
|
debug!("moving shape left");
|
|
|
|
|
|
|
|
*gp -= RelativePosition::new(1, 0);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Movement::Right => {
|
|
|
|
|
|
|
|
grid_positions.iter_mut().for_each(|mut gp| {
|
|
|
|
|
|
|
|
debug!("moving shape right");
|
|
|
|
|
|
|
|
*gp += RelativePosition::new(1, 0);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Movement::Down => {
|
|
|
|
|
|
|
|
grid_positions.iter_mut().for_each(|mut gp| {
|
|
|
|
|
|
|
|
debug!("moving shape down");
|
|
|
|
|
|
|
|
*gp -= RelativePosition::new(0, 1);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Movement::Rotate => {
|
|
|
|
|
|
|
|
orientations.iter_mut().for_each(|mut o| {
|
|
|
|
|
|
|
|
debug!("rotating shape");
|
|
|
|
|
|
|
|
*o = o.rotated();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|