Compare commits

..

3 Commits

@ -1,11 +1,13 @@
#![feature(try_blocks)] #![feature(try_blocks)]
// Bevy basically forces "complex types" with Querys // Bevy basically forces "complex types" with Querys
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use bevy::{ use bevy::{
asset::RenderAssetUsages, asset::RenderAssetUsages,
math::FloatOrd, math::{FloatOrd},
render::{ render::{
mesh::MeshAabb,
camera::{ImageRenderTarget, RenderTarget}, camera::{ImageRenderTarget, RenderTarget},
render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages}, render_resource::{Extent3d, TextureDimension, TextureFormat, TextureUsages},
view::RenderLayers, view::RenderLayers,
@ -40,10 +42,11 @@ fn main() {
.add_systems( .add_systems(
Startup, Startup,
( (
init_world, init_tetris,
init_debug_ui, init_debug_ui,
init_cameras, init_cameras,
init_ui.after(init_cameras), init_ui.after(init_cameras),
init_battler,
), ),
) )
// Input and basic systems // Input and basic systems
@ -78,6 +81,11 @@ fn main() {
.run_if(any_component_changed::<Line>) .run_if(any_component_changed::<Line>)
.after(clear_line), .after(clear_line),
assert_grid_position_uniqueness.run_if(any_component_changed::<GridPosition>), assert_grid_position_uniqueness.run_if(any_component_changed::<GridPosition>),
sync_health
.run_if(any_component_changed::<Health>.or(any_component_added::<Health>)),
damage_on_place_shape.run_if(any_component_removed::<Shape>),
damage_on_clear_line.run_if(any_component_removed::<LineBlock>),
damage_over_time.run_if(clock_cycle(5.0))
), ),
) )
// UI systems // UI systems
@ -90,6 +98,7 @@ fn main() {
sync_singleton_to_ui::<Shape>.run_if(any_component_changed::<Shape>), sync_singleton_to_ui::<Shape>.run_if(any_component_changed::<Shape>),
), ),
) )
.add_observer(deal_damage)
.run(); .run();
} }
@ -244,6 +253,9 @@ impl Display for Score {
} }
} }
#[derive(Component, Debug)]
struct Health(f32);
/// ShapesBuffer resource stores non-active shapes /// ShapesBuffer resource stores non-active shapes
#[derive(Resource, Debug, Default)] #[derive(Resource, Debug, Default)]
struct ShapesBuffer { struct ShapesBuffer {
@ -276,7 +288,7 @@ impl Display for ShapeStore {
#[derive(Component)] #[derive(Component)]
struct GridBackground; struct GridBackground;
fn init_world( fn init_tetris(
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>>,
@ -313,6 +325,54 @@ fn init_world(
}); });
} }
#[derive(Component, Debug)]
struct Protagonist;
#[derive(Component, Debug)]
struct Enemy;
fn init_battler(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
{
let mat = materials.add(ColorMaterial {
color: BLUE.into(),
..default()
});
let mesh = meshes.add(Ellipse::new(SCALE, SCALE * 2.0));
let t = Transform::from_xyz(-3.0 * SCALE, 0.0, 0.0);
commands.spawn((
Mesh2d(mesh),
MeshMaterial2d(mat),
t,
BATTLER,
Health(100.0),
Name::new("Protagonist"),
Protagonist,
));
}
{
let mat = materials.add(ColorMaterial {
color: RED.into(),
..default()
});
let mesh = meshes.add(Ellipse::new(SCALE, SCALE));
let t = Transform::from_xyz(3.0 * SCALE, 0.0, 0.0);
commands.spawn((
Mesh2d(mesh),
MeshMaterial2d(mat),
t,
BATTLER,
Health(100.0),
Name::new("ENEMY"),
Enemy,
));
}
}
#[derive(Resource, Default)] #[derive(Resource, Default)]
struct OutputImages { struct OutputImages {
tetris: Handle<Image>, tetris: Handle<Image>,
@ -366,6 +426,7 @@ fn init_cameras(
Camera { Camera {
order: 1, order: 1,
target, target,
clear_color: ClearColorConfig::Custom(WHITE.into()),
..default() ..default()
}, },
TETRIS, TETRIS,
@ -374,7 +435,8 @@ fn init_cameras(
} }
{ {
let img = render_target_image((512, 512)); let (width, height) = (SCALE as u32 * Y_MAX as u32, SCALE as u32 * X_MAX as u32);
let img = render_target_image((width, height));
let target = { let target = {
let handle = images.add(img); let handle = images.add(img);
output_images.battler = handle.clone(); output_images.battler = handle.clone();
@ -390,6 +452,7 @@ fn init_cameras(
Camera { Camera {
order: 2, order: 2,
target, target,
clear_color: ClearColorConfig::Custom(WHITE.into()),
..default() ..default()
}, },
BATTLER, BATTLER,
@ -439,53 +502,57 @@ fn init_ui(mut commands: Commands, output_images: Res<OutputImages>, images: Res
}); });
}); });
commands.spawn(( commands
Node { .spawn((
align_self: AlignSelf::End,
justify_self: JustifySelf::Center,
border: UiRect::all(Val::Px(5.0)),
..default()
},
BorderColor(WHITE.into()),
)).with_children(|parent| {
let img = images.get(&output_images.tetris).unwrap();
parent.spawn((
Node { Node {
width: Val::Px(img.size_f32().x * 0.75), align_self: AlignSelf::End,
height: Val::Px(img.size_f32().y * 0.75), justify_self: JustifySelf::Center,
..default() border: UiRect::all(Val::Px(5.0)),
},
ImageNode {
image: output_images.tetris.clone(),
image_mode: NodeImageMode::Stretch,
..default() ..default()
}, },
)); BorderColor(WHITE.into()),
}); ))
.with_children(|parent| {
let img = images.get(&output_images.tetris).unwrap();
parent.spawn((
Node {
width: Val::Px(img.size_f32().x * 0.75),
height: Val::Px(img.size_f32().y * 0.75),
..default()
},
ImageNode {
image: output_images.tetris.clone(),
image_mode: NodeImageMode::Stretch,
..default()
},
));
});
commands.spawn(( commands
Node { .spawn((
align_self: AlignSelf::Start,
justify_self: JustifySelf::Center,
border: UiRect::all(Val::Px(5.0)),
..default()
},
BorderColor(WHITE.into()),
)).with_children(|parent| {
let img = images.get(&output_images.battler).unwrap();
parent.spawn((
Node { Node {
width: Val::Px(img.size_f32().x * 0.5), align_self: AlignSelf::Start,
height: Val::Px(img.size_f32().y * 0.5), justify_self: JustifySelf::Center,
..default() border: UiRect::all(Val::Px(5.0)),
},
ImageNode {
image: output_images.battler.clone(),
image_mode: NodeImageMode::Stretch,
..default() ..default()
}, },
)); BorderColor(WHITE.into()),
}); ))
.with_children(|parent| {
let img = images.get(&output_images.battler).unwrap();
parent.spawn((
Node {
width: Val::Px(img.size_f32().x * 0.75),
height: Val::Px(img.size_f32().y * 0.75),
..default()
},
ImageNode {
image: output_images.battler.clone(),
image_mode: NodeImageMode::Stretch,
..default()
},
));
});
commands commands
.spawn(( .spawn((
@ -910,11 +977,11 @@ fn clear_line(
if cleared_lines.contains(&l.0) { if cleared_lines.contains(&l.0) {
// Move to the N-offset line number (top, top-1, etc) // Move to the N-offset line number (top, top-1, etc)
let offset = original_cleared_lines_len - cleared_lines.len(); let offset = original_cleared_lines_len - cleared_lines.len();
info!("Moving line {:?}->{:?}", l.0, Y_MAX - 1 - offset); debug!("Moving line {:?}->{:?}", l.0, Y_MAX - 1 - offset);
l.0 = Y_MAX - 1 - offset; l.0 = Y_MAX - 1 - offset;
cleared_lines.pop(); cleared_lines.pop();
} else { } else {
info!("Moving line {:?}->{:?}", l.0, l.0 - cleared_lines.len()); debug!("Moving line {:?}->{:?}", l.0, l.0 - cleared_lines.len());
l.0 -= cleared_lines.len(); l.0 -= cleared_lines.len();
} }
}); });
@ -952,7 +1019,10 @@ enum Movement {
// TODO: When out of bounds left/right, try to move piece away from wall // TODO: When out of bounds left/right, try to move piece away from wall
fn movement( fn movement(
trigger: Trigger<Movement>, trigger: Trigger<Movement>,
mut grid_positions: Query<&mut GridPosition, Or<(With<ShapeBlock>, With<ShapeBlocks>, Without<LineBlock>)>>, mut grid_positions: Query<
&mut GridPosition,
Or<(With<ShapeBlock>, With<ShapeBlocks>, Without<LineBlock>)>,
>,
mut shape: Query<&mut Shape>, mut shape: Query<&mut Shape>,
inactive: Query<&GridPosition, (Without<ShapeBlock>, Without<ShapeBlocks>, With<LineBlock>)>, inactive: Query<&GridPosition, (Without<ShapeBlock>, Without<ShapeBlocks>, With<LineBlock>)>,
mut commands: Commands, mut commands: Commands,
@ -1100,9 +1170,84 @@ fn update_next_shapes(mut buffer: ResMut<ShapesBuffer>) {
} }
fn assert_grid_position_uniqueness( fn assert_grid_position_uniqueness(
grid_positions: Query<&GridPosition, (Without<GridBackground>, Without<Shape>)> grid_positions: Query<&GridPosition, (Without<GridBackground>, Without<Shape>)>,
) { ) {
grid_positions.iter_combinations().for_each(|[a, b]| { grid_positions.iter_combinations().for_each(|[a, b]| {
assert_ne!(a, b, "Two entities are in the same grid position!"); assert_ne!(a, b, "Two entities are in the same grid position!");
}); });
} }
fn sync_health(
query: Query<(Entity, &Health, &Mesh2d), Or<(Changed<Health>, Added<Health>)>>,
parent: Query<&Children>,
meshes: Res<Assets<Mesh>>,
mut texts: Query<&mut Text2d>,
mut commands: Commands,
) {
query.iter().for_each(|(e, h, m)| {
if let Some(child) = parent
.iter_descendants(e)
.find(|child| texts.contains(*child))
{
info!("Updating health");
let mut t = texts.get_mut(child).unwrap();
t.0 = format!("{}", h.0);
} else {
info!("Creating health display");
commands.entity(e).with_children(|parent| {
let mesh = meshes.get(&m.0).unwrap();
let aabb = mesh.compute_aabb().unwrap();
let offset = Vec3::new(0.0, aabb.half_extents.y + 10.0, 0.0);
parent.spawn((
Text2d(format!("{}", h.0)),
TextColor(BLACK.into()),
Transform::from_translation(offset),
BATTLER,
));
});
}
})
}
#[derive(Event)]
struct Damage {
quantity: f32
}
fn deal_damage(
trigger: Trigger<Damage>,
mut healths: Query<&mut Health>
) {
healths.get_mut(trigger.target()).unwrap().0 -= trigger.event().quantity
}
fn damage_on_place_shape(
mut events: RemovedComponents<Shape>,
enemies: Query<Entity, With<Enemy>>,
mut commands: Commands,
) {
events.read().for_each(|_| {
enemies.iter().for_each(|e| {
commands.entity(e).trigger(Damage { quantity: 1.0 });
});
});
}
fn damage_on_clear_line(
mut events: RemovedComponents<LineBlock>,
enemies: Query<Entity, With<Enemy>>,
mut commands: Commands,
) {
events.read().for_each(|_| {
enemies.iter().for_each(|e| {
commands.entity(e).trigger(Damage { quantity: 1.0 });
});
});
}
fn damage_over_time(
protagonist: Single<Entity, With<Protagonist>>,
mut commands: Commands,
) {
commands.entity(*protagonist).trigger(Damage { quantity: 1.0 });
}

Loading…
Cancel
Save