You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
martian-chess/src/display2d.rs

231 lines
7.4 KiB
Rust

use crate::{
game::{Board, BoardIndex, Piece, SelectedTile},
prelude::*,
};
const SCALE: f32 = 4.0;
const TILE_SIZE: f32 = 16.0;
pub struct Display2dPlugin;
impl Plugin for Display2dPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Startup,
(initialize_camera, load_spritesheet).run_if(in_state(GameState::Loading)),
)
.add_systems(
Update,
(
initialize_board.run_if(resource_added::<SpriteSheet>()),
draw_board
.run_if(resource_exists::<SpriteSheet>())
.run_if(resource_changed::<Board>()) // TODO: run_if(in_state(Display2d))
.run_if(any_with_component::<BoardIndex>()),
cursor_position.run_if(in_state(GameState::Display2d)),
selected.run_if(resource_changed::<SelectedTile>()),
),
)
.add_systems(OnEnter(GameState::Display2d), (activate, draw_board));
}
}
/// Sprite sheet Resource for later reference
#[derive(Debug, Resource)]
struct SpriteSheet {
handle: Handle<TextureAtlas>,
}
/// Marker component for the 2d board entity
#[derive(Debug, Component)]
struct Board2d;
/// Marker for 2d piece entities
#[derive(Debug, Component)]
struct Piece2d;
/// STARTUP: Initialize 2d gameplay Camera
fn initialize_camera(mut commands: Commands) {
commands.spawn(Camera2dBundle {
camera: Camera {
is_active: false,
..default()
},
..default()
});
}
/// STARTUP: Load sprite sheet and insert texture atlas
fn load_spritesheet(
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
server: Res<AssetServer>,
mut commands: Commands,
) {
let atlas = TextureAtlas::from_grid(
server.load("sprites.png"),
Vec2::new(TILE_SIZE, TILE_SIZE),
5,
1,
None,
None,
);
commands.insert_resource(SpriteSheet {
handle: texture_atlases.add(atlas),
});
}
/// STARTUP: Initialize the board for representation
fn initialize_board(sprite_sheet: Option<Res<SpriteSheet>>, mut commands: Commands) {
if let Some(sprite_sheet) = sprite_sheet {
commands
.spawn((
SpatialBundle {
transform: Transform::from_xyz(
-SCALE * TILE_SIZE * 7.0 / 2.0, // TODO: WHY???
-SCALE * TILE_SIZE * 3.0 / 2.0, // Why 7 and 3??
0.0,
),
..default()
},
Board2d,
))
.with_children(|parent| {
for i in 0..32 {
let x = i % 8;
let y = i / 8;
let s = (x % 2) ^ (y % 2);
let transform = Transform::from_scale(Vec3::splat(SCALE)).with_translation(
Vec3::new(SCALE * 16.0 * x as f32, SCALE * 16.0 * y as f32, 0.0),
);
let sprite = TextureAtlasSprite::new(s);
let texture_atlas = sprite_sheet.handle.clone();
let index = BoardIndex { x, y };
// Rectangle
parent.spawn((
SpriteSheetBundle {
texture_atlas,
sprite,
transform,
..default()
},
index,
));
}
});
}
}
fn draw_board(
board: Option<Res<Board>>,
mut commands: Commands,
tiles: Query<(&Transform, &BoardIndex)>,
pieces: Query<Entity, With<Piece2d>>,
root: Query<Entity, With<Board2d>>,
sprite_sheet: Option<Res<SpriteSheet>>,
) {
if let (Some(board), Some(sprite_sheet)) = (board, sprite_sheet) {
commands.entity(root.single()).with_children(|parent| {
info!("Board and sprite sheet ready, drawing board");
board
.pieces()
.iter()
.filter_map(|(board_index, piece)| {
tiles.iter().find_map(|(transform, this_index)| {
(*this_index == *board_index).then(|| (piece, transform))
})
})
.for_each(|(piece, transform)| {
let texture_atlas = sprite_sheet.handle.clone();
let s = match piece {
Piece::Queen => 2,
Piece::Drone => 3,
Piece::Pawn => 4,
};
let sprite = TextureAtlasSprite::new(s);
// TODO: transform is slightly different, set sprite
parent.spawn((
piece.clone(),
SpriteSheetBundle {
texture_atlas,
sprite,
transform: Transform {
..transform.clone()
},
..default()
},
Piece2d,
));
});
});
}
}
fn activate(
mut cameras: Query<&mut Camera, With<Camera2d>>,
mut boards: Query<&mut Visibility, With<Board2d>>,
) {
cameras.iter_mut().for_each(|mut camera| {
camera.is_active = true;
});
boards.iter_mut().for_each(|mut visibility| {
*visibility = Visibility::Visible;
});
}
fn cursor_position(
mut events: EventReader<CursorMoved>,
sprite_q: Query<(
&TextureAtlasSprite,
&Handle<TextureAtlas>,
&GlobalTransform,
&BoardIndex,
)>,
camera_q: Query<(&Camera, &GlobalTransform), With<Camera2d>>,
atlases: Res<Assets<TextureAtlas>>,
mut selected: ResMut<SelectedTile>,
) {
events.iter().for_each(|CursorMoved { position, .. }| {
if let Some(position) = camera_q
.iter()
.find_map(|(camera, transform)| camera.viewport_to_world_2d(transform, *position))
{
let idx = sprite_q.iter().find_map(
|(TextureAtlasSprite { index, anchor, .. }, handle, transform, board_index)| {
// Implementation credit goes to the sprite bevy_mod_picking backend
// TODO: Upstream changes
let pos = transform.translation();
let size = {
let sprite_size = atlases
.get(handle)
.map(|atlas| atlas.textures.get(*index).expect("Get this rect"))
.expect("get this rect")
.size();
let (transform_scale, _, _) = transform.to_scale_rotation_translation();
sprite_size * transform_scale.truncate()
};
let center = pos.truncate() - (anchor.as_vec() * size);
let rect = Rect::from_center_half_size(center, size / 2.0);
rect.contains(position).then_some(board_index)
},
);
if selected.idx != idx.cloned() {
selected.idx = idx.cloned();
}
}
});
}
fn selected(selected: Res<SelectedTile>, board: Res<Board>) {
selected.idx.iter().for_each(|idx| {
info!("Selected Tile: {:?} contains {:?}", idx, board.at(idx));
});
}