Compare commits

..

2 Commits

Author SHA1 Message Date
Elijah Voigt cea796325e Detecting when to deactivate a piece
At least when there are no other blocks on the board...
1 month ago
Elijah Voigt a668b946f2 Placeholder for lines 1 month ago

@ -1,17 +1,14 @@
#![allow(clippy::single_match)]
#![allow(clippy::type_complexity)]
use super::*;
// TODO:
// - When shape asset is updated, shape layout update in real time
// - Debug: gizmo outline entities
// - conditional enable/disable
// - entities without mesh should draw empty cross
// - Debug: gizmo draw relationship lines
// - conditional enable/disable
// - Debug: show FPS
// - conditional enable/disable
// - Line entities
// - Debug: Nametags
// - If it has a Name component, display that at the same Translation
// - Disable shape when placed
@ -31,6 +28,7 @@ impl Plugin for BlocksPlugin {
app.init_asset::<ShapeAsset>()
.init_asset_loader::<ShapeAssetLoader>()
.add_message::<Movement>()
.add_message::<ShapeEvent>()
.add_systems(OnEnter(Loading(true)), load_assets.run_if(run_once))
.add_systems(
OnEnter(GameState::Setup),
@ -49,6 +47,7 @@ impl Plugin for BlocksPlugin {
propogate_relative_position.run_if(any_component_changed::<RelativePosition>),
handle_kb_input.run_if(on_message::<KeyboardInput>),
handle_movement.run_if(on_message::<Movement>),
handle_shape_event.run_if(on_message::<ShapeEvent>),
),
)
.add_observer(add_shape)
@ -108,11 +107,11 @@ struct GridPosition {
#[derive(Debug, Error)]
enum GridPositionError {
#[error("X > X_MAX")]
XOver,
HitRight,
#[error("X < 0")]
XUnder,
HitLeft,
#[error("Y < 0")]
YUnder,
HitFloor,
#[error("Y < 0 && X < 0")]
BothUnder,
}
@ -130,17 +129,17 @@ impl GridPosition {
// Both underflow
(None, None) => Err(GridPositionError::BothUnder),
// Just y underflows
(None, Some(_)) => Err(GridPositionError::XUnder),
(None, Some(_)) => Err(GridPositionError::HitLeft),
// y underflow or x overflow
(Some(x), None) => match x {
0..X_MAX => Err(GridPositionError::YUnder),
_ => Err(GridPositionError::XOver),
}
0..X_MAX => Err(GridPositionError::HitFloor),
_ => Err(GridPositionError::HitRight),
},
// both good, may x overflow
(Some(x), Some(y)) => match x {
0..X_MAX => Ok(GridPosition { x, y }),
_ => Err(GridPositionError::XOver),
}
0..X_MAX => Ok(GridPosition { x, y }),
_ => Err(GridPositionError::HitRight),
},
}
}
}
@ -379,7 +378,7 @@ fn setup_grid(
mut materials: ResMut<Assets<ColorMaterial>>,
mut checklist: ResMut<SetupChecklist>,
) {
let m: Mesh = Rectangle::new(SCALE, SCALE).into();
let m: Mesh = Rectangle::new(SCALE * 0.9, SCALE * 0.9).into();
let mh: Handle<Mesh> = meshes.add(m);
let m2d = Mesh2d(mh);
let c: Color = WHITE.with_alpha(0.5).into();
@ -387,10 +386,15 @@ fn setup_grid(
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 {
for y in 0..Y_MAX {
// Spawn this line
let gp = GridPosition { x: 0, y };
commands.spawn((gp, Line(y as u8), LineBlocks::default(), t));
// Spawn the grid squares for this line
for x in 0..X_MAX {
let gp = GridPosition { x, y };
commands.spawn((gp, m2d.clone(), mat.clone(), t.clone()));
commands.spawn((gp, m2d.clone(), mat.clone(), t));
}
}
@ -407,6 +411,20 @@ struct ShapeBlock(Entity);
#[relationship_target(relationship = ShapeBlock)]
struct ShapeBlocks(Vec<Entity>);
/// Line number component
#[derive(Component)]
struct Line(u8);
/// Blocks <- Line Relationship
#[derive(Component)]
#[relationship(relationship_target = LineBlocks)]
struct LineBlock(Entity);
/// Line -> Blocks Relationship
#[derive(Component, Default)]
#[relationship_target(relationship = LineBlock)]
struct LineBlocks(Vec<Entity>);
/// Event handler for transforming a handle component into a thing
fn add_shape(
event: On<Add, AssetComponent<ShapeAsset>>,
@ -520,6 +538,12 @@ enum Movement {
Right,
}
/// Things that can happen to a shape
#[derive(Debug, Message, PartialEq)]
enum ShapeEvent {
Deactivate,
}
/// Handle KeyBoard input
/// Nothing is handled directly in this method,
/// All key presses result in either a message, event, or state change
@ -573,40 +597,54 @@ impl Orientation {
fn handle_movement(
mut moves: MessageReader<Movement>,
mut shapes: Query<(&ShapeLayout, &mut GridPosition, &mut Orientation)>,
mut writer: MessageWriter<ShapeEvent>,
) {
moves.read().for_each(|m| {
shapes.iter_mut().for_each(|(shape_layout, mut gp, mut o)| {
'outer: for m in moves.read() {
for (shape_layout, mut gp, mut o) in shapes.iter_mut() {
// First determine if proposed positions are valid
// Determine next orientation
let proposed_orientation = if *m == Movement::Rotate {
o.rotated()
} else {
*o
let proposed_orientation = match *m == Movement::Rotate {
true => o.rotated(),
false => *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()),
let offset: RelativePosition = match m {
Movement::Left => (-1, 0).into(),
Movement::Right => (1, 0).into(),
Movement::Down => (0, -1).into(),
Movement::Rotate => (0, 0).into(),
};
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!
// TODO: See if the proposed position would collide with another block
// If would collide downward, deactivate shape
// Check that new positions are all valid for all blocks
for pos in shape_layout.positions(proposed_orientation).iter() {
match gp.add(*pos).unwrap().add(offset) {
Err(e) => {
match e {
GridPositionError::HitFloor => {
writer.write(ShapeEvent::Deactivate);
}
_ => (), // Other errors are just ignored
}
// Either way skip the rest of this iteration of the loop
continue 'outer;
}
Ok(_) => (), // Ignore OKs as no [bad] news is good news
}
} else {
// Invalid move!
}
});
// If those checks went well, commit the proposed changes
*gp = gp.add(offset).unwrap();
*o = proposed_orientation;
}
}
}
fn handle_shape_event(mut reader: MessageReader<ShapeEvent>) {
reader.read().for_each(|msg| match msg {
ShapeEvent::Deactivate => warn!("TODO: Deactivate shape"),
});
}

Loading…
Cancel
Save