|
|
|
|
@ -32,7 +32,10 @@ impl Plugin for BlocksPlugin {
|
|
|
|
|
.init_asset_loader::<ShapeAssetLoader>()
|
|
|
|
|
.add_message::<Movement>()
|
|
|
|
|
.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, setup_grid),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
(
|
|
|
|
|
@ -43,6 +46,7 @@ impl Plugin for BlocksPlugin {
|
|
|
|
|
),
|
|
|
|
|
propogate_orientation.run_if(any_component_changed::<Orientation>),
|
|
|
|
|
propogate_grid_position.run_if(any_component_changed::<GridPosition>),
|
|
|
|
|
propogate_relative_position.run_if(any_component_changed::<RelativePosition>),
|
|
|
|
|
handle_kb_input.run_if(on_message::<KeyboardInput>),
|
|
|
|
|
handle_movement.run_if(on_message::<Movement>),
|
|
|
|
|
),
|
|
|
|
|
@ -52,7 +56,7 @@ impl Plugin for BlocksPlugin {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const SCALE: f32 = 20.0;
|
|
|
|
|
pub(crate) const SCALE: f32 = 20.0;
|
|
|
|
|
const X_MAX: usize = 10;
|
|
|
|
|
const Y_MAX: usize = 20;
|
|
|
|
|
|
|
|
|
|
@ -101,11 +105,42 @@ struct GridPosition {
|
|
|
|
|
pub y: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
|
enum GridPositionError {
|
|
|
|
|
#[error("X > X_MAX")]
|
|
|
|
|
XOver,
|
|
|
|
|
#[error("X < 0")]
|
|
|
|
|
XUnder,
|
|
|
|
|
#[error("Y < 0")]
|
|
|
|
|
YUnder,
|
|
|
|
|
#[error("Y < 0 && X < 0")]
|
|
|
|
|
BothUnder,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl GridPosition {
|
|
|
|
|
fn with_relative_offset(&self, other: &RelativePosition) -> GridPosition {
|
|
|
|
|
GridPosition {
|
|
|
|
|
x: self.x.checked_add_signed(other.x).unwrap(),
|
|
|
|
|
y: self.y.checked_add_signed(other.y).unwrap(),
|
|
|
|
|
self.add(*other).unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add(&self, other: RelativePosition) -> Result<GridPosition, GridPositionError> {
|
|
|
|
|
let x = self.x.checked_add_signed(other.x);
|
|
|
|
|
let y = self.y.checked_add_signed(other.y);
|
|
|
|
|
|
|
|
|
|
match (x, y) {
|
|
|
|
|
// Both underflow
|
|
|
|
|
(None, None) => Err(GridPositionError::BothUnder),
|
|
|
|
|
// Just y underflows
|
|
|
|
|
(None, Some(_)) => Err(GridPositionError::XUnder),
|
|
|
|
|
// y underflow or x overflow
|
|
|
|
|
(Some(x), None) => match x {
|
|
|
|
|
0..X_MAX => Err(GridPositionError::YUnder),
|
|
|
|
|
_ => Err(GridPositionError::XOver),
|
|
|
|
|
}
|
|
|
|
|
// both good, may x overflow
|
|
|
|
|
(Some(x), Some(y)) => match x {
|
|
|
|
|
0..X_MAX => Ok(GridPosition { x, y }),
|
|
|
|
|
_ => Err(GridPositionError::XOver),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -118,31 +153,6 @@ 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
|
|
|
|
|
#[derive(Component, PartialEq, Debug, Clone, Copy)]
|
|
|
|
|
pub(crate) struct RelativePosition {
|
|
|
|
|
@ -257,8 +267,9 @@ impl ShapeLayout {
|
|
|
|
|
// TODO: Just hard-code this
|
|
|
|
|
ShapeLayout {
|
|
|
|
|
matrix: self.rotated_matrix(Orientation::Right),
|
|
|
|
|
symmetrical: false
|
|
|
|
|
}.rotated_matrix(Orientation::Right)
|
|
|
|
|
symmetrical: false,
|
|
|
|
|
}
|
|
|
|
|
.rotated_matrix(Orientation::Right)
|
|
|
|
|
}
|
|
|
|
|
Orientation::Left => {
|
|
|
|
|
// The main algorithm
|
|
|
|
|
@ -352,6 +363,7 @@ fn setup_blocks(
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
mut checklist: ResMut<SetupChecklist>,
|
|
|
|
|
) {
|
|
|
|
|
// TODO: WARN: THIS WILL CAUSE ISSUES WHEN WE LOAD MULTIPLE SHAPES
|
|
|
|
|
let h: Handle<ShapeAsset> = server
|
|
|
|
|
.get_handle(all_assets.handles[0].path().unwrap())
|
|
|
|
|
.unwrap();
|
|
|
|
|
@ -361,6 +373,30 @@ fn setup_blocks(
|
|
|
|
|
checklist.spawn_shape = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn setup_grid(
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
|
|
|
|
mut checklist: ResMut<SetupChecklist>,
|
|
|
|
|
) {
|
|
|
|
|
let m: Mesh = Rectangle::new(SCALE, SCALE).into();
|
|
|
|
|
let mh: Handle<Mesh> = meshes.add(m);
|
|
|
|
|
let m2d = Mesh2d(mh);
|
|
|
|
|
let c: Color = WHITE.with_alpha(0.5).into();
|
|
|
|
|
let ch: Handle<ColorMaterial> = materials.add(c);
|
|
|
|
|
let mat = MeshMaterial2d(ch);
|
|
|
|
|
let t = Transform::from_xyz(0.0, 0.0, -1.0);
|
|
|
|
|
|
|
|
|
|
for x in 0..X_MAX {
|
|
|
|
|
for y in 0..Y_MAX {
|
|
|
|
|
let gp = GridPosition { x, y };
|
|
|
|
|
commands.spawn((gp, m2d.clone(), mat.clone(), t.clone()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
checklist.spawn_grid = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Blocks <- Shape Relationship
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
#[relationship(relationship_target = ShapeBlocks)]
|
|
|
|
|
@ -427,7 +463,9 @@ fn update_grid_position_transform(
|
|
|
|
|
>,
|
|
|
|
|
) {
|
|
|
|
|
query.iter_mut().for_each(|(gp, mut t)| {
|
|
|
|
|
t.translation = gp.into();
|
|
|
|
|
let new_t: Vec3 = gp.into();
|
|
|
|
|
t.translation.x = new_t.x;
|
|
|
|
|
t.translation.y = new_t.y;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -439,28 +477,42 @@ fn propogate_grid_position(
|
|
|
|
|
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;
|
|
|
|
|
*gp = parent_gp.with_relative_offset(rp);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// When a shape's orientation changes, the blocks need to move
|
|
|
|
|
/// When a block's relative position changes, update it's grid position
|
|
|
|
|
fn propogate_relative_position(
|
|
|
|
|
mut children: Query<
|
|
|
|
|
(&mut GridPosition, &RelativePosition, &ShapeBlock),
|
|
|
|
|
Changed<RelativePosition>,
|
|
|
|
|
>,
|
|
|
|
|
parent: Query<&GridPosition, Without<ShapeBlock>>,
|
|
|
|
|
) {
|
|
|
|
|
children.iter_mut().for_each(|(mut gp, rp, sb)| {
|
|
|
|
|
let parent_gp = parent.get(sb.0).unwrap();
|
|
|
|
|
*gp = parent_gp.with_relative_offset(rp);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// When a shape's orientation changes, the blocks need to move so assign new relative positions
|
|
|
|
|
fn propogate_orientation(
|
|
|
|
|
parent: Query<(&GridPosition, &Orientation, &ShapeLayout, &ShapeBlocks), Changed<Orientation>>,
|
|
|
|
|
mut children: Query<&mut GridPosition, Without<ShapeBlocks>>,
|
|
|
|
|
parent: Query<(&Orientation, &ShapeLayout, &ShapeBlocks), Changed<Orientation>>,
|
|
|
|
|
mut children: Query<&mut RelativePosition>,
|
|
|
|
|
) {
|
|
|
|
|
parent.iter().for_each(|(parent_gp, parent_o, sl, sbs)| {
|
|
|
|
|
parent.iter().for_each(|(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;
|
|
|
|
|
let mut this_rp = children.get_mut(e).unwrap();
|
|
|
|
|
*this_rp = rp;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Movement message used to propose/plan a move
|
|
|
|
|
#[derive(Debug, Message)]
|
|
|
|
|
#[derive(Debug, Message, PartialEq)]
|
|
|
|
|
enum Movement {
|
|
|
|
|
Rotate,
|
|
|
|
|
Left,
|
|
|
|
|
@ -520,33 +572,41 @@ impl Orientation {
|
|
|
|
|
|
|
|
|
|
fn handle_movement(
|
|
|
|
|
mut moves: MessageReader<Movement>,
|
|
|
|
|
mut grid_positions: Query<&mut GridPosition, With<ShapeBlocks>>,
|
|
|
|
|
mut orientations: Query<&mut Orientation, With<ShapeBlocks>>,
|
|
|
|
|
mut shapes: Query<(&ShapeLayout, &mut GridPosition, &mut Orientation)>,
|
|
|
|
|
) {
|
|
|
|
|
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();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
moves.read().for_each(|m| {
|
|
|
|
|
shapes.iter_mut().for_each(|(shape_layout, mut gp, mut o)| {
|
|
|
|
|
// First determine if proposed positions are valid
|
|
|
|
|
// Determine next orientation
|
|
|
|
|
let proposed_orientation = if *m == Movement::Rotate {
|
|
|
|
|
o.rotated()
|
|
|
|
|
} else {
|
|
|
|
|
*o
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// And next position
|
|
|
|
|
let proposed_position = match m {
|
|
|
|
|
Movement::Left => gp.add((-1, 0).into()),
|
|
|
|
|
Movement::Right => gp.add((1, 0).into()),
|
|
|
|
|
Movement::Down => gp.add((0, -1).into()),
|
|
|
|
|
Movement::Rotate => Ok(gp.clone()),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Ok(pp) = proposed_position {
|
|
|
|
|
// Check that new positions are all valid
|
|
|
|
|
if shape_layout
|
|
|
|
|
.positions(proposed_orientation)
|
|
|
|
|
.iter()
|
|
|
|
|
.all(|rp| pp.add(*rp).is_ok()) {
|
|
|
|
|
// then commit the movement
|
|
|
|
|
*gp = pp;
|
|
|
|
|
*o = proposed_orientation;
|
|
|
|
|
} else {
|
|
|
|
|
// invalid position!
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Invalid move!
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|