|
|
|
@ -1,9 +1,18 @@
|
|
|
|
|
|
|
|
use bevy::{
|
|
|
|
|
|
|
|
input::{
|
|
|
|
|
|
|
|
mouse::{MouseButtonInput, MouseMotion},
|
|
|
|
|
|
|
|
ButtonState,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
window::{PrimaryWindow, WindowResized},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
///
|
|
|
|
|
|
|
|
/// TODO: Pick up and move pieces!
|
|
|
|
/// TODO: Custom Asset: SpriteSheetAtlas Mapper
|
|
|
|
/// TODO: Custom Asset: SpriteSheetAtlas Mapper
|
|
|
|
/// TODO: Handle Cursor!
|
|
|
|
/// TODO: Handle Cursor!
|
|
|
|
///
|
|
|
|
///
|
|
|
|
use crate::{
|
|
|
|
use crate::{
|
|
|
|
game::{Board, BoardIndex, Piece, SelectedTile},
|
|
|
|
game::{ActiveTile, Board, BoardIndex, Piece},
|
|
|
|
prelude::*,
|
|
|
|
prelude::*,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@ -16,19 +25,32 @@ impl Plugin for Display2dPlugin {
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
fn build(&self, app: &mut App) {
|
|
|
|
app.add_systems(
|
|
|
|
app.add_systems(
|
|
|
|
Startup,
|
|
|
|
Startup,
|
|
|
|
(initialize_camera, load_spritesheet).run_if(in_state(GameState::Loading)),
|
|
|
|
(initialize_camera, load_spritesheet, set_background)
|
|
|
|
|
|
|
|
.run_if(in_state(GameState::Loading)),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
(
|
|
|
|
initialize_board.run_if(resource_added::<SpriteSheet>()),
|
|
|
|
initialize_board.run_if(resource_added::<SpriteSheet>()),
|
|
|
|
|
|
|
|
active_tile.run_if(in_state(GameState::Display2d)),
|
|
|
|
|
|
|
|
menu::exit_to_menu.run_if(in_state(GameState::Display2d)),
|
|
|
|
|
|
|
|
select_piece
|
|
|
|
|
|
|
|
.run_if(in_state(GameState::Display2d))
|
|
|
|
|
|
|
|
.run_if(not(any_with_component::<game::Selected>())),
|
|
|
|
|
|
|
|
move_piece
|
|
|
|
|
|
|
|
.run_if(in_state(GameState::Display2d))
|
|
|
|
|
|
|
|
.run_if(any_with_component::<game::Selected>()),
|
|
|
|
|
|
|
|
place_piece
|
|
|
|
|
|
|
|
.run_if(in_state(GameState::Display2d))
|
|
|
|
|
|
|
|
.run_if(any_with_component::<game::Selected>()),
|
|
|
|
|
|
|
|
cancel_place
|
|
|
|
|
|
|
|
.run_if(in_state(GameState::Display2d))
|
|
|
|
|
|
|
|
.run_if(any_with_component::<game::Selected>()),
|
|
|
|
|
|
|
|
update_background.run_if(on_event::<WindowResized>()),
|
|
|
|
draw_board
|
|
|
|
draw_board
|
|
|
|
.run_if(resource_exists::<SpriteSheet>())
|
|
|
|
.run_if(resource_exists::<SpriteSheet>())
|
|
|
|
.run_if(resource_changed::<Board>()) // TODO: run_if(in_state(Display2d))
|
|
|
|
.run_if(any_component_removed::<game::Selected>()) // trigger if item was de-selected
|
|
|
|
.run_if(any_with_component::<BoardIndex>()),
|
|
|
|
.run_if(any_with_component::<BoardIndex>()),
|
|
|
|
cursor_position.run_if(in_state(GameState::Display2d)),
|
|
|
|
|
|
|
|
selected.run_if(resource_changed::<SelectedTile>()),
|
|
|
|
|
|
|
|
menu::exit_to_menu.run_if(in_state(GameState::Display2d)),
|
|
|
|
|
|
|
|
),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.add_systems(OnEnter(GameState::Display2d), (activate, draw_board))
|
|
|
|
.add_systems(OnEnter(GameState::Display2d), (activate, draw_board))
|
|
|
|
@ -53,6 +75,9 @@ struct Piece2d;
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
struct Display2dCamera;
|
|
|
|
struct Display2dCamera;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
|
|
|
struct BackgroundImage;
|
|
|
|
|
|
|
|
|
|
|
|
/// STARTUP: Initialize 2d gameplay Camera
|
|
|
|
/// STARTUP: Initialize 2d gameplay Camera
|
|
|
|
fn initialize_camera(mut commands: Commands) {
|
|
|
|
fn initialize_camera(mut commands: Commands) {
|
|
|
|
commands.spawn((
|
|
|
|
commands.spawn((
|
|
|
|
@ -87,6 +112,43 @@ fn load_spritesheet(
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn set_background(
|
|
|
|
|
|
|
|
server: Res<AssetServer>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
window: Query<&Window, With<PrimaryWindow>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
|
|
|
|
BackgroundImage,
|
|
|
|
|
|
|
|
SpriteBundle {
|
|
|
|
|
|
|
|
texture: server.load("mars-daybreak.png"),
|
|
|
|
|
|
|
|
sprite: Sprite {
|
|
|
|
|
|
|
|
custom_size: Some(Vec2 {
|
|
|
|
|
|
|
|
x: window.single().width(),
|
|
|
|
|
|
|
|
y: window.single().height(),
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
..default()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn update_background(
|
|
|
|
|
|
|
|
mut sprites: Query<&mut Sprite, With<BackgroundImage>>,
|
|
|
|
|
|
|
|
mut events: EventReader<WindowResized>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.for_each(|&WindowResized { width, height, .. }| {
|
|
|
|
|
|
|
|
sprites.iter_mut().for_each(|mut sprite| {
|
|
|
|
|
|
|
|
sprite.custom_size = Some(Vec2 {
|
|
|
|
|
|
|
|
x: width,
|
|
|
|
|
|
|
|
y: height,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// STARTUP: Initialize the board for representation
|
|
|
|
/// STARTUP: Initialize the board for representation
|
|
|
|
fn initialize_board(sprite_sheet: Option<Res<SpriteSheet>>, mut commands: Commands) {
|
|
|
|
fn initialize_board(sprite_sheet: Option<Res<SpriteSheet>>, mut commands: Commands) {
|
|
|
|
if let Some(sprite_sheet) = sprite_sheet {
|
|
|
|
if let Some(sprite_sheet) = sprite_sheet {
|
|
|
|
@ -136,24 +198,26 @@ fn initialize_board(sprite_sheet: Option<Res<SpriteSheet>>, mut commands: Comman
|
|
|
|
|
|
|
|
|
|
|
|
fn draw_board(
|
|
|
|
fn draw_board(
|
|
|
|
board: Option<Res<Board>>,
|
|
|
|
board: Option<Res<Board>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
sprite_sheet: Option<Res<SpriteSheet>>,
|
|
|
|
|
|
|
|
root: Query<Entity, With<Board2d>>,
|
|
|
|
tiles: Query<(&Transform, &BoardIndex)>,
|
|
|
|
tiles: Query<(&Transform, &BoardIndex)>,
|
|
|
|
pieces: Query<Entity, With<Piece2d>>,
|
|
|
|
pieces: Query<Entity, With<Piece2d>>,
|
|
|
|
root: Query<Entity, With<Board2d>>,
|
|
|
|
mut commands: Commands,
|
|
|
|
sprite_sheet: Option<Res<SpriteSheet>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if let (Some(board), Some(sprite_sheet)) = (board, sprite_sheet) {
|
|
|
|
if let (Some(board), Some(sprite_sheet)) = (board, sprite_sheet) {
|
|
|
|
|
|
|
|
pieces
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.for_each(|entity| commands.entity(entity).despawn_recursive());
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
commands.entity(root.single()).with_children(|parent| {
|
|
|
|
info!("Board and sprite sheet ready, drawing board");
|
|
|
|
|
|
|
|
board
|
|
|
|
board
|
|
|
|
.pieces()
|
|
|
|
.pieces()
|
|
|
|
.iter()
|
|
|
|
.iter()
|
|
|
|
.filter_map(|(board_index, piece)| {
|
|
|
|
.filter_map(|(board_index, piece)| {
|
|
|
|
tiles.iter().find_map(|(transform, this_index)| {
|
|
|
|
tiles.iter().find_map(|(transform, this_index)| {
|
|
|
|
(*this_index == *board_index).then(|| (piece, transform))
|
|
|
|
(*this_index == *board_index).then(|| (piece, transform, this_index))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.for_each(|(piece, transform)| {
|
|
|
|
.for_each(|(piece, transform, index)| {
|
|
|
|
let texture_atlas = sprite_sheet.handle.clone();
|
|
|
|
let texture_atlas = sprite_sheet.handle.clone();
|
|
|
|
let s = match piece {
|
|
|
|
let s = match piece {
|
|
|
|
Piece::Queen => 2,
|
|
|
|
Piece::Queen => 2,
|
|
|
|
@ -173,6 +237,7 @@ fn draw_board(
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Piece2d,
|
|
|
|
Piece2d,
|
|
|
|
|
|
|
|
index.clone(),
|
|
|
|
));
|
|
|
|
));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
@ -203,7 +268,7 @@ fn deactivate(
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn cursor_position(
|
|
|
|
fn active_tile(
|
|
|
|
mut events: EventReader<CursorMoved>,
|
|
|
|
mut events: EventReader<CursorMoved>,
|
|
|
|
sprite_q: Query<(
|
|
|
|
sprite_q: Query<(
|
|
|
|
&TextureAtlasSprite,
|
|
|
|
&TextureAtlasSprite,
|
|
|
|
@ -213,7 +278,7 @@ fn cursor_position(
|
|
|
|
)>,
|
|
|
|
)>,
|
|
|
|
camera_q: Query<(&Camera, &GlobalTransform), With<Camera2d>>,
|
|
|
|
camera_q: Query<(&Camera, &GlobalTransform), With<Camera2d>>,
|
|
|
|
atlases: Res<Assets<TextureAtlas>>,
|
|
|
|
atlases: Res<Assets<TextureAtlas>>,
|
|
|
|
mut selected: ResMut<SelectedTile>,
|
|
|
|
mut active: ResMut<ActiveTile>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
events.iter().for_each(|CursorMoved { position, .. }| {
|
|
|
|
events.iter().for_each(|CursorMoved { position, .. }| {
|
|
|
|
if let Some(position) = camera_q
|
|
|
|
if let Some(position) = camera_q
|
|
|
|
@ -242,15 +307,111 @@ fn cursor_position(
|
|
|
|
rect.contains(position).then_some(board_index)
|
|
|
|
rect.contains(position).then_some(board_index)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
);
|
|
|
|
if selected.idx != idx.cloned() {
|
|
|
|
if active.idx != idx.cloned() {
|
|
|
|
selected.idx = idx.cloned();
|
|
|
|
active.idx = idx.cloned();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn selected(selected: Res<SelectedTile>, board: Res<Board>) {
|
|
|
|
fn select_piece(
|
|
|
|
selected.idx.iter().for_each(|idx| {
|
|
|
|
mut events: EventReader<MouseButtonInput>,
|
|
|
|
info!("Selected Tile: {:?} contains {:?}", idx, board.at(idx));
|
|
|
|
pieces: Query<(Entity, &BoardIndex), With<Piece2d>>,
|
|
|
|
});
|
|
|
|
active: Res<ActiveTile>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
mut writer: EventWriter<game::GameEvent>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.filter_map(|MouseButtonInput { button, state, .. }| {
|
|
|
|
|
|
|
|
active.idx.as_ref().and_then(|index| {
|
|
|
|
|
|
|
|
pieces.iter().find_map(|(entity, board_idx)| {
|
|
|
|
|
|
|
|
(board_idx == index).then_some((entity, button, state))
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.filter_map(|(entity, button, state)| {
|
|
|
|
|
|
|
|
((*button, *state) == (MouseButton::Left, ButtonState::Pressed)).then_some(entity)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.for_each(|entity| {
|
|
|
|
|
|
|
|
commands
|
|
|
|
|
|
|
|
.entity(entity)
|
|
|
|
|
|
|
|
.insert(game::Selected)
|
|
|
|
|
|
|
|
.remove_parent();
|
|
|
|
|
|
|
|
writer.send(game::GameEvent::SelectPiece);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn move_piece(
|
|
|
|
|
|
|
|
window: Query<&Window, With<PrimaryWindow>>,
|
|
|
|
|
|
|
|
mut query: Query<&mut Transform, (With<game::Selected>, With<Piece2d>)>,
|
|
|
|
|
|
|
|
camera_query: Query<(&Camera, &GlobalTransform), With<Display2dCamera>>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
query.iter_mut().for_each(|mut t| {
|
|
|
|
|
|
|
|
let (camera, camera_t) = camera_query.single();
|
|
|
|
|
|
|
|
if let Some(pos) = window
|
|
|
|
|
|
|
|
.single()
|
|
|
|
|
|
|
|
.cursor_position()
|
|
|
|
|
|
|
|
.and_then(|cursor| camera.viewport_to_world_2d(camera_t, cursor))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
t.translation.x = pos.x;
|
|
|
|
|
|
|
|
t.translation.y = pos.y;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
/// TODO: Only place piece if spot is valid move for the selected entity
|
|
|
|
|
|
|
|
///
|
|
|
|
|
|
|
|
fn place_piece(
|
|
|
|
|
|
|
|
mut events: EventReader<MouseButtonInput>,
|
|
|
|
|
|
|
|
current: Query<(Entity, &BoardIndex), (With<game::Selected>, With<Piece2d>)>,
|
|
|
|
|
|
|
|
pieces: Query<&BoardIndex, (Without<game::Selected>, With<Piece2d>)>,
|
|
|
|
|
|
|
|
active: Res<ActiveTile>,
|
|
|
|
|
|
|
|
mut board: ResMut<Board>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
mut writer: EventWriter<game::GameEvent>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.filter_map(|MouseButtonInput { button, state, .. }| {
|
|
|
|
|
|
|
|
if (*button, *state) == (MouseButton::Left, ButtonState::Pressed) {
|
|
|
|
|
|
|
|
active.idx.as_ref()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.filter_map(|idx| {
|
|
|
|
|
|
|
|
(!pieces.iter().any(|board_index| board_index == idx)).then_some((
|
|
|
|
|
|
|
|
current.single().0,
|
|
|
|
|
|
|
|
current.single().1,
|
|
|
|
|
|
|
|
idx,
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.for_each(|(entity, from, to)| match board.move_piece(from, to) {
|
|
|
|
|
|
|
|
Ok(()) => {
|
|
|
|
|
|
|
|
commands.entity(entity).remove::<game::Selected>();
|
|
|
|
|
|
|
|
writer.send(game::GameEvent::PlacePiece);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(game::GameError::NullMove) => writer.send(game::GameEvent::PlacePiece),
|
|
|
|
|
|
|
|
Err(game::GameError::InvalidMove) => warn!("Invalid move!"),
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn cancel_place(
|
|
|
|
|
|
|
|
mut events: EventReader<MouseButtonInput>,
|
|
|
|
|
|
|
|
current: Query<Entity, (With<game::Selected>, With<Piece2d>)>,
|
|
|
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
|
|
|
mut writer: EventWriter<game::GameEvent>,
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
events
|
|
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
|
|
.filter(|MouseButtonInput { button, state, .. }| {
|
|
|
|
|
|
|
|
(*button, *state) == (MouseButton::Right, ButtonState::Pressed)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.for_each(|_| {
|
|
|
|
|
|
|
|
let entity = current.single();
|
|
|
|
|
|
|
|
commands.entity(entity).remove::<game::Selected>();
|
|
|
|
|
|
|
|
writer.send(game::GameEvent::PlacePiece);
|
|
|
|
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|