Saving place. Broken but don't want to go further...

main
Elijah C. Voigt 2 years ago
parent b86aea043e
commit 2fb524643d

@ -222,7 +222,17 @@ fn selected_position(
} }
fn debug_piece( fn debug_piece(
query: Query<(Entity, Option<&Side>, Option<&Piece>, Option<&BoardIndex>, Option<&display3d::Animating>, Option<&Selected>), With<Piece>>, query: Query<
(
Entity,
Option<&Side>,
Option<&Piece>,
Option<&BoardIndex>,
Option<&display3d::Animating>,
Option<&Selected>,
),
With<Piece>,
>,
pointer: Res<display3d::PiecePointer>, pointer: Res<display3d::PiecePointer>,
mut root: Query<(&mut Text, &mut Visibility), With<DebugPiece>>, mut root: Query<(&mut Text, &mut Visibility), With<DebugPiece>>,
// mut commands: Commands, // mut commands: Commands,
@ -239,11 +249,11 @@ fn debug_piece(
*vis = Visibility::Inherited; *vis = Visibility::Inherited;
}); });
} }
}, }
_ => { _ => {
root.iter_mut().for_each(|(_, mut vis)| { root.iter_mut().for_each(|(_, mut vis)| {
*vis = Visibility::Hidden; *vis = Visibility::Hidden;
}); });
} }
} }
} }

@ -78,8 +78,9 @@ impl Plugin for Display3dPlugin {
.run_if( .run_if(
in_state(debug::DebugState::Enabled) in_state(debug::DebugState::Enabled)
.and_then(on_event::<MouseMotion>()) .and_then(on_event::<MouseMotion>())
.or_else(just_pressed(MouseButton::Left)) .or_else(just_pressed(MouseButton::Left)),
).before(select), )
.before(select),
select select
.run_if(in_state(GameState::Play)) .run_if(in_state(GameState::Play))
.run_if(in_state(DisplayState::Display3d)) .run_if(in_state(DisplayState::Display3d))
@ -118,7 +119,7 @@ impl Plugin for Display3dPlugin {
( (
animate_title_light_in.run_if(in_state(DissolvingAnimation::In)), animate_title_light_in.run_if(in_state(DissolvingAnimation::In)),
animate_title_light_out.run_if(in_state(DissolvingAnimation::Out)), animate_title_light_out.run_if(in_state(DissolvingAnimation::Out)),
) ),
) )
.add_systems( .add_systems(
OnEnter(GameState::Intro), OnEnter(GameState::Intro),
@ -133,11 +134,12 @@ impl Plugin for Display3dPlugin {
) )
.add_systems( .add_systems(
Update, Update,
setup_dissolve_materials.run_if(in_state(GameState::Intro)) setup_dissolve_materials
.run_if(in_state(GameState::Intro))
.run_if( .run_if(
any_component_added::<Handle<StandardMaterial>>() any_component_added::<Handle<StandardMaterial>>()
.or_else(any_component_changed::<Handle<StandardMaterial>>()), .or_else(any_component_changed::<Handle<StandardMaterial>>()),
), ),
) )
.add_systems( .add_systems(
OnEnter(GameState::Play), OnEnter(GameState::Play),
@ -186,7 +188,7 @@ enum DissolvingAnimation {
#[default] #[default]
None, None,
In, In,
Out Out,
} }
#[derive(Debug, Resource, Clone)] #[derive(Debug, Resource, Clone)]
@ -487,7 +489,10 @@ fn set_board_model(
} }
fn set_title_model( fn set_title_model(
mut titles: Query<(&mut Handle<Scene>, &mut Transform, &mut Visibility), (Added<TitleText>, With<Display3d>)>, mut titles: Query<
(&mut Handle<Scene>, &mut Transform, &mut Visibility),
(Added<TitleText>, With<Display3d>),
>,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
tweaks: Res<Assets<Tweaks>>, tweaks: Res<Assets<Tweaks>>,
tweaks_file: Res<tweak::GameTweaks>, tweaks_file: Res<tweak::GameTweaks>,
@ -495,29 +500,31 @@ fn set_title_model(
let tweak = tweaks let tweak = tweaks
.get(tweaks_file.handle.clone()) .get(tweaks_file.handle.clone())
.expect("Load tweakfile"); .expect("Load tweakfile");
titles.iter_mut().for_each(|(mut handle, mut transform, mut visibility)| { titles
info!("Setting title model"); .iter_mut()
let assets_handle = tweak .for_each(|(mut handle, mut transform, mut visibility)| {
.get_handle::<Gltf>("display3d_models_assets_file") info!("Setting title model");
.unwrap(); let assets_handle = tweak
let gltf = gltfs.get(assets_handle).expect("Load GLTF content"); .get_handle::<Gltf>("display3d_models_assets_file")
*handle = gltf .unwrap();
.named_scenes let gltf = gltfs.get(assets_handle).expect("Load GLTF content");
.get( *handle = gltf
tweak .named_scenes
.get::<String>("display3d_models_scenes_title") .get(
.unwrap() tweak
.as_str(), .get::<String>("display3d_models_scenes_title")
) .unwrap()
.expect("Game title model") .as_str(),
.clone(); )
.expect("Game title model")
.clone();
transform.translation -= Vec3::Y * 0.5; transform.translation -= Vec3::Y * 0.5;
transform.rotate_local_z(std::f32::consts::PI); transform.rotate_local_z(std::f32::consts::PI);
transform.rotate_local_y(std::f32::consts::PI / 2.0); transform.rotate_local_y(std::f32::consts::PI / 2.0);
*visibility = Visibility::Hidden; *visibility = Visibility::Hidden;
}); });
} }
/// Given a board index returns the Vec3 location in space /// Given a board index returns the Vec3 location in space
@ -682,8 +689,8 @@ fn select(
selections.send(game::Selection(**board_index)); selections.send(game::Selection(**board_index));
} }
}); });
}, }
_ => () _ => (),
} }
} }
@ -724,24 +731,21 @@ fn entity_pointer(
}) })
.filter_map(|(entity, hit)| { .filter_map(|(entity, hit)| {
// Find this entity in the set of selectable entities // Find this entity in the set of selectable entities
selectable selectable.iter().find_map(|e| {
.iter() let hit_check = {
.find_map(|e| { // This entity was hit (tile hitboxes)
let hit_check = { let primary = entity == e;
// This entity was hit (tile hitboxes)
let primary = entity == e; // A child was hit (pieces)
let secondary =
// A child was hit (pieces) children.iter_descendants(e).any(|child| child == entity);
let secondary = children
.iter_descendants(e) primary || secondary
.any(|child| child == entity); };
primary || secondary // Return the board index of this piece
}; hit_check.then_some((e, hit.clone()))
})
// Return the board index of this piece
hit_check.then_some((e, hit.clone()))
})
}) })
// Compare the distance of all hits, choosing the closest one // Compare the distance of all hits, choosing the closest one
.min_by(|(_, hit_a), (_, hit_b)| { .min_by(|(_, hit_a), (_, hit_b)| {
@ -840,11 +844,12 @@ fn pick_up(
children.iter_descendants(entity).for_each(|child| { children.iter_descendants(entity).for_each(|child| {
info!(" Child: {:?}", child); info!(" Child: {:?}", child);
if let Ok((name, mut player)) = players.get_mut(child) { if let Ok((name, mut player)) = players.get_mut(child) {
let pickup_animation = format!( let pickup_animation =
"display3d_models_animations_pick_up_{}", format!("display3d_models_animations_pick_up_{}", name.as_str(),);
name.as_str(), info!(
"Picking up {:?} ({:?}) {:?} {:?}",
name, entity, piece, pickup_animation
); );
info!("Picking up {:?} ({:?}) {:?} {:?}", name, entity, piece, pickup_animation);
let pickup_handle = gltf let pickup_handle = gltf
.named_animations .named_animations
.get( .get(
@ -854,10 +859,7 @@ fn pick_up(
.as_str(), .as_str(),
) )
.expect("Pickup Animation"); .expect("Pickup Animation");
let idle_animation = format!( let idle_animation = format!("display3d_models_animations_idle_{}", name.as_str());
"display3d_models_animations_idle_{}",
name.as_str()
);
let idle_handle = gltf let idle_handle = gltf
.named_animations .named_animations
.get( .get(
@ -890,10 +892,7 @@ fn pick_up(
fn put_down( fn put_down(
mut events: RemovedComponents<game::Selected>, mut events: RemovedComponents<game::Selected>,
mut query: Query< mut query: Query<Entity, (With<game::Piece>, With<game::Selectable>, With<Display3d>)>,
Entity,
(With<game::Piece>, With<game::Selectable>, With<Display3d>),
>,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
children: Query<&Children>, children: Query<&Children>,
mut players: Query<(&Name, &mut AnimationPlayer)>, mut players: Query<(&Name, &mut AnimationPlayer)>,
@ -915,7 +914,8 @@ fn put_down(
children.iter_descendants(entity).for_each(|child| { children.iter_descendants(entity).for_each(|child| {
if let Ok((name, mut player)) = players.get_mut(child) { if let Ok((name, mut player)) = players.get_mut(child) {
info!("Putting down {:?}", entity); info!("Putting down {:?}", entity);
let putdown_animation = format!("display3d_models_animations_put_down_{}", name.as_str()); let putdown_animation =
format!("display3d_models_animations_put_down_{}", name.as_str());
let putdown_handle = gltf let putdown_handle = gltf
.named_animations .named_animations
.get( .get(
@ -935,7 +935,10 @@ fn put_down(
) )
.set_repeat(RepeatAnimation::Never); .set_repeat(RepeatAnimation::Never);
} else { } else {
info!("Clip {:?}({:?}) not compatible with {:?}", putdown_animation, putdown_clip, name); info!(
"Clip {:?}({:?}) not compatible with {:?}",
putdown_animation, putdown_clip, name
);
} }
} else { } else {
info!("Clip not found"); info!("Clip not found");
@ -1481,4 +1484,4 @@ fn animate_title_light_out(
} }
}) })
}); });
} }

@ -118,6 +118,13 @@ impl Piece {
.iter(), .iter(),
} }
} }
fn moves_at(&self, from: &BoardIndex) -> Vec<BoardIndex> {
self.moves().map(|(x, y)| {
*from + (*x, *y)
})
.collect()
}
} }
#[derive(Debug, Component, Clone, PartialEq)] #[derive(Debug, Component, Clone, PartialEq)]
@ -280,12 +287,29 @@ impl std::ops::Add<(isize, isize)> for BoardIndex {
fn add(self, (a, b): (isize, isize)) -> Self { fn add(self, (a, b): (isize, isize)) -> Self {
BoardIndex { BoardIndex {
x: self.x.saturating_add_signed(a), x: self.x.saturating_add_signed(a).max(7),
y: self.y.saturating_add_signed(b), y: self.y.saturating_add_signed(b).max(3),
} }
} }
} }
impl std::ops::Add<(usize, usize)> for BoardIndex {
type Output = BoardIndex;
fn add(self, (a, b): (usize, usize)) -> Self {
BoardIndex {
x: self.x.saturating_add(a).max(7),
y: self.y.saturating_add(b).max(3),
}
}
}
impl From<(usize, usize)> for BoardIndex {
fn from((x, y): (usize, usize)) -> BoardIndex {
BoardIndex { x, y }
}
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum MoveType { pub(crate) enum MoveType {
Valid, Valid,
@ -320,24 +344,33 @@ impl Board {
fn from_ascii(art: &str) -> Board { fn from_ascii(art: &str) -> Board {
use Piece::*; use Piece::*;
let mut inner: Vec<Vec<Option<Piece>>> = vec![Vec::with_capacity(8), Vec::with_capacity(8), Vec::with_capacity(8), Vec::with_capacity(8)]; let mut inner: Vec<Vec<Option<Piece>>> = vec![
Vec::with_capacity(8),
Vec::with_capacity(8),
Vec::with_capacity(8),
Vec::with_capacity(8),
];
let mut index = BoardIndex { x: 0, y: 3 }; let mut index = BoardIndex { x: 0, y: 3 };
art.chars().for_each(|c| { art.chars().for_each(|c| {
match c { match c {
'q' => { 'q' | 'Q' => {
inner[index.y].push(Some(Queen)); inner[index.y].push(Some(Queen));
} }
'd' => { 'd' | 'D' => {
inner[index.y].push(Some(Drone)); inner[index.y].push(Some(Drone));
}, }
'p' => { 'p' | 'P' => {
inner[index.y].push(Some(Pawn)); inner[index.y].push(Some(Pawn));
}, }
'.' => { '.' | '#' => {
inner[index.y].push(None); inner[index.y].push(None);
}, }
'\n' => { '\n' => {
assert_eq!(inner[index.y].len(), 8, "Each row must be 8 characters long!"); assert_eq!(
inner[index.y].len(),
8,
"Each row must be 8 characters long!"
);
index.y -= 1; index.y -= 1;
} }
_ => { _ => {
@ -356,7 +389,8 @@ impl Board {
r#".....dqq r#".....dqq
dpp..pdq dpp..pdq
qdp..ppd qdp..ppd
qqd....."#) qqd....."#,
)
} }
/// Show all pieces on one side of the board /// Show all pieces on one side of the board
@ -501,9 +535,10 @@ impl Board {
) -> Option<MoveType> { ) -> Option<MoveType> {
// Iterate over the piece's moves // Iterate over the piece's moves
piece piece
.moves() .moves_at(&from)
.iter()
// Find if the given `to` move is one of those // Find if the given `to` move is one of those
.find(|(x, y)| to == from + (*x, *y)) .find(|BoardIndex { x, y }| to == from + (*x, *y))
// Determine if this is valid/legal in this situation // Determine if this is valid/legal in this situation
.and_then(|_| { .and_then(|_| {
let dest_at = self.at(to); let dest_at = self.at(to);
@ -584,17 +619,18 @@ impl Board {
/// Returns the possible moves the piece at this tile can make. /// Returns the possible moves the piece at this tile can make.
pub(crate) fn valid_moves(&self, current_board_index: BoardIndex) -> HashSet<BoardIndex> { pub(crate) fn valid_moves(&self, current_board_index: BoardIndex) -> HashSet<BoardIndex> {
tiles() if let Some(piece) = self.at(current_board_index) {
.filter_map(|(board_index, _)| { piece.moves_at(&current_board_index).iter().filter_map(|move_index| {
// Get the move type (or none if totally invalid) // Get the move type (or none if totally invalid)
self.at(current_board_index).and_then(|piece| { match self.move_type(*piece, current_board_index, *move_index) {
match self.move_type(*piece, current_board_index, board_index) { None | Some(MoveType::Invalid) => None,
None | Some(MoveType::Invalid) => None, _ => Some(*move_index),
_ => Some(board_index), }
}
})
}) })
.collect() .collect()
} else {
HashSet::new()
}
} }
pub(crate) fn current_epoch(&self) -> usize { pub(crate) fn current_epoch(&self) -> usize {
@ -606,14 +642,161 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn test_01() { fn pawn_simple_moves() {
let mut board = Board::new(); let board = Board::from_ascii(
print!("{}", board); r#"........
println!("{:?}", board.at(BoardIndex { x: 0, y: 0 })); .#.#....
println!("{:?}", board.at(BoardIndex { x: 1, y: 0 })); ..p.....
println!("{:?}", board.at(BoardIndex { x: 2, y: 0 })); .#.#...."#,
println!("{:?}", board.at(BoardIndex { x: 3, y: 0 })); );
todo!() let given = board.valid_moves((2, 1).into());
let expected: HashSet<BoardIndex> =
HashSet::from([(1, 0).into(), (3, 0).into(), (1, 2).into(), (3, 2).into()]);
assert_eq!(expected, given, "Basic pawn moves");
}
#[test]
fn drone_simple_moves() {
let board = Board::from_ascii(
r#"..#.....
..#.....
##d##...
..#....."#,
);
let given = board.valid_moves((2, 1).into());
let expected: HashSet<BoardIndex> = HashSet::from([
(2, 0).into(),
(2, 2).into(),
(2, 3).into(),
(0, 1).into(),
(1, 1).into(),
(3, 1).into(),
(4, 1).into(),
]);
assert_eq!(expected, given, "Basic drone moves");
}
#[test]
fn queen_simple_moves() {
let board = Board::from_ascii(
r#"...###..
####q###
...###..
..#.#.#."#,
);
let given = board.valid_moves((4, 2).into());
let expected: HashSet<BoardIndex> = HashSet::from([
(0, 2).into(),
(1, 0).into(),
(1, 2).into(),
(2, 0).into(),
(2, 2).into(),
(3, 1).into(),
(3, 2).into(),
(3, 3).into(),
(4, 0).into(),
(4, 1).into(),
(4, 3).into(),
(5, 1).into(),
(5, 2).into(),
(5, 3).into(),
(6, 0).into(),
(6, 2).into(),
(7, 0).into(),
(7, 2).into(),
]);
assert_eq!(expected, given, "Basic queen moves");
}
#[test]
fn empty_moves() {
let board = Board::from_ascii(
r#"........
.p....q.
........
...d...."#,
);
let given = board.valid_moves((2, 1).into());
let expected: HashSet<BoardIndex> = HashSet::from([]);
assert_eq!(expected, given, "Empty moves");
}
/// When a piece is blocked on all sides by friendly, cannot move
#[test]
fn blocking_friendly() {
let board = Board::from_ascii(
r#"p.......
..dp....
.pqp....
.ppp...."#,
);
// Check queen
{
let given = board.valid_moves((2, 1).into());
let expected: HashSet<BoardIndex> = HashSet::from([(1, 2).into()]);
assert_eq!(expected, given, "Mostly surrounded queen, some moves");
}
// Check pawn
{
let given = board.valid_moves((3, 1).into());
let expected: HashSet<BoardIndex> = HashSet::from([(4, 0).into(), (4, 2).into()]);
assert_eq!(expected, given, "Partially blocked pawn, some moves");
}
// Check drone
{
let given = board.valid_moves((2, 2).into());
let expected: HashSet<BoardIndex> =
HashSet::from([(0, 2).into(), (1, 2).into(), (2, 3).into()]);
assert_eq!(expected, given, "Partially blocked drone, some moves");
}
}
/// When an enemy is on one side, can move to capture just that piece
#[test]
fn case_03() {
let board = Board::from_ascii(
r#"........
........
ppp.....
pq..p..p"#,
);
{
let given = board.valid_moves((1, 0).into());
let expected: HashSet<BoardIndex> =
HashSet::from([(2, 0).into(), (3, 0).into(), (4, 0).into()]);
assert_eq!(
expected, given,
"Mostly blocked queen, moves include captures"
);
}
}
/// Can move up to but not over friendlies
#[test]
fn case_02() {
let board = Board::from_ascii(
r#"......qq
dpp#d#d.
qd##qppd
qq.###.."#,
);
{
let given = board.valid_moves((4, 1).into());
let expected: HashSet<BoardIndex> =
HashSet::from([
(3,0).into(), (4,0).into(), (5,0).into(), (2, 1).into(), (2, 3).into(), (3, 1).into(), (5, 1).into(), (3, 2).into(), (5, 2).into()
]);
assert_eq!(
expected, given,
"Mostly blocked queen, moves include captures"
);
}
} }
} }

@ -99,9 +99,7 @@ pub(crate) fn intersects3d(ray: &Ray3d, mesh: &Mesh, gt: &GlobalTransform) -> Op
&& triangle.normal().dot(c1) > 0.0 && triangle.normal().dot(c1) > 0.0
&& triangle.normal().dot(c2) > 0.0 && triangle.normal().dot(c2) > 0.0
}; };
hit.then_some(Hit3d { hit.then_some(Hit3d { distance: d })
distance: d,
})
} else { } else {
None None
} }

@ -22,8 +22,7 @@ impl Plugin for UiPlugin {
), ),
) )
.add_systems(OnEnter(GameState::Intro), show_click_prompt) .add_systems(OnEnter(GameState::Intro), show_click_prompt)
.add_systems(OnExit(GameState::Title), hide_click_prompt) .add_systems(OnExit(GameState::Title), hide_click_prompt);
;
} }
} }
@ -189,14 +188,14 @@ fn init_prompts(mut commands: Commands) {
}); });
} }
fn show_click_prompt( fn show_click_prompt(mut query: Query<(&mut Visibility, &Prompt)>) {
mut query: Query<(&mut Visibility, &Prompt)>, query
) { .iter_mut()
query.iter_mut().for_each(|(mut vis, _)| *vis = Visibility::Inherited); .for_each(|(mut vis, _)| *vis = Visibility::Inherited);
} }
fn hide_click_prompt( fn hide_click_prompt(mut query: Query<(&mut Visibility, &Prompt)>) {
mut query: Query<(&mut Visibility, &Prompt)>, query
) { .iter_mut()
query.iter_mut().for_each(|(mut vis, _)| *vis = Visibility::Hidden); .for_each(|(mut vis, _)| *vis = Visibility::Hidden);
} }

Loading…
Cancel
Save