use std::sync::{Arc, Mutex}; use anyhow::Result; use engine::prelude::{ cgmath::{vec2, vec3, Vector2, Vector3}, *, }; use crate::{game::MillGame, objects::Objects}; #[derive(Debug)] pub struct BoardSlot { pub x: usize, pub y: usize, pub z: usize, state: Mutex, pub position: Vector2, pub slot_marker: Option, } impl BoardSlot { pub const SLOT_MARKER_SIZE: f32 = 1.5; pub fn new( x: usize, y: usize, z: usize, position: Vector2, world: &mut World, ) -> Result { let mut marker = AssetHandler::create(world).empty_entity(); let meshes = vec![Self::create_mesh( world.resources.get::().device(), world.resources.get::(), Color::Black, )?]; let draw = Draw::new(meshes); marker.insert_component(draw); let mut location = Location::from_entity(&mut marker); location.set_position(position.extend(MillGame::OFFSET_TO_BOARD)); marker.insert_component(location); Ok(Self { x, y, z, state: Mutex::new(BoardSlotState::default()), position, slot_marker: Some(world.add_entity(marker)?), }) } pub fn invalid(x: usize, y: usize, z: usize) -> Self { Self { x, y, z, state: Mutex::new(BoardSlotState::Invalid), position: vec2(0.0, 0.0), slot_marker: None, } } fn create_mesh( device: &Arc, engine_settings: &EngineSettings, color: Color, ) -> Result { let mut mesh = AssetMesh::new(device, engine_settings.graphics_info()?.render_type)?; let vertex_buffer = Buffer::builder() .set_data(&Objects::create_flat_quad([ vec2(-Self::SLOT_MARKER_SIZE * 0.5, -Self::SLOT_MARKER_SIZE * 0.5), vec2(Self::SLOT_MARKER_SIZE * 0.5, -Self::SLOT_MARKER_SIZE * 0.5), vec2(Self::SLOT_MARKER_SIZE * 0.5, Self::SLOT_MARKER_SIZE * 0.5), vec2(-Self::SLOT_MARKER_SIZE * 0.5, Self::SLOT_MARKER_SIZE * 0.5), ])) .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .build(device.clone())?; let a: [f32; 3] = color.into(); mesh.add_primitive( vertex_buffer, None, None, PrimitiveMaterial { color: [a[0], a[1], a[2], 1.0], metallic_factor: 0.2, emissive_factor: [0.2, 0.2, 0.2], roughness_factor: 0.8, alpha_mode: AlphaMode::Opaque, alpha_cut_off: 0.5, }, true, )?; Ok(mesh) } pub fn state(&self) -> BoardSlotState { *self.state.lock().unwrap() } pub fn set_state(&self, state: BoardSlotState) { *self.state.lock().unwrap() = state; } pub fn valid(&self) -> bool { *self.state.lock().unwrap() != BoardSlotState::Invalid } pub fn is_empty(&self) -> bool { *self.state.lock().unwrap() == BoardSlotState::Empty } pub fn white(&self) -> bool { match *self.state.lock().unwrap() { BoardSlotState::White(_) => true, _ => false, } } pub fn black(&self) -> bool { match *self.state.lock().unwrap() { BoardSlotState::Black(_) => true, _ => false, } } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum BoardSlotState { Invalid, Empty, Black(Entity), White(Entity), } impl Default for BoardSlotState { fn default() -> Self { Self::Empty } } pub struct Board { _board: Entity, center: Vector3, _connection_lines: Vec, slots: [[[BoardSlot; 3]; 3]; 3], white_start_slots: [Vector2; 9], black_start_slots: [Vector2; 9], } impl Board { pub fn new(world: &mut World) -> Result { let mut board_entity = AssetHandler::create(world).empty_entity(); let meshes = vec![Self::create_board_base(&world)?]; let draw = Draw::new(meshes); board_entity.insert_component(draw); let location = Location::from_entity(&mut board_entity); board_entity.insert_component(location); let board = world.add_entity(board_entity)?; let slots = [ [ [ BoardSlot::new(0, 0, 0, vec2(-15.0, -15.0), world)?, // 0 0 0 BoardSlot::new(0, 0, 1, vec2(-10.0, -10.0), world)?, // 0 0 1 BoardSlot::new(0, 0, 2, vec2(-5.0, -5.0), world)?, // 0 0 2 ], [ BoardSlot::new(0, 1, 0, vec2(-15.0, 0.0), world)?, // 0 1 0 BoardSlot::new(0, 1, 1, vec2(-10.0, 0.0), world)?, // 0 1 1 BoardSlot::new(0, 1, 2, vec2(-5.0, 0.0), world)?, // 0 1 2 ], [ BoardSlot::new(0, 2, 0, vec2(-15.0, 15.0), world)?, // 0 2 0 BoardSlot::new(0, 2, 1, vec2(-10.0, 10.0), world)?, // 0 2 1 BoardSlot::new(0, 2, 2, vec2(-5.0, 5.0), world)?, // 0 2 2 ], ], [ [ BoardSlot::new(1, 0, 0, vec2(0.0, -15.0), world)?, // 1 0 0 BoardSlot::new(1, 0, 1, vec2(0.0, -10.0), world)?, // 1 0 1 BoardSlot::new(1, 0, 2, vec2(0.0, -5.0), world)?, // 1 0 2 ], [ BoardSlot::invalid(1, 1, 0), // 1 1 0 BoardSlot::invalid(1, 1, 1), // 1 1 1 BoardSlot::invalid(1, 1, 2), // 1 1 2 ], [ BoardSlot::new(1, 2, 0, vec2(0.0, 15.0), world)?, // 1 2 0 BoardSlot::new(1, 2, 1, vec2(0.0, 10.0), world)?, // 1 2 1 BoardSlot::new(1, 2, 2, vec2(0.0, 5.0), world)?, // 1 2 2 ], ], [ [ BoardSlot::new(2, 0, 0, vec2(15.0, -15.0), world)?, // 2 0 0 BoardSlot::new(2, 0, 1, vec2(10.0, -10.0), world)?, // 2 0 1 BoardSlot::new(2, 0, 2, vec2(5.0, -5.0), world)?, // 2 0 2 ], [ BoardSlot::new(2, 1, 0, vec2(15.0, 0.0), world)?, // 2 1 0 BoardSlot::new(2, 1, 1, vec2(10.0, 0.0), world)?, // 2 1 1 BoardSlot::new(2, 1, 2, vec2(5.0, 0.0), world)?, // 2 1 2 ], [ BoardSlot::new(2, 2, 0, vec2(15.0, 15.0), world)?, // 2 2 0 BoardSlot::new(2, 2, 1, vec2(10.0, 10.0), world)?, // 2 2 1 BoardSlot::new(2, 2, 2, vec2(5.0, 5.0), world)?, // 2 2 2 ], ], ]; let distance = 20.0; let white_start_slots = (16..=16) .step_by(4) .map(|x| vec2(x as f32, distance)) .collect() .try_into() .unwrap(); let black_start_slots = (16..=16) .step_by(4) .map(|x| vec2(x as f32, -distance)) .collect() .try_into() .unwrap(); Ok(Self { _board: board, center: vec3(0.0, 0.0, 0.0), _connection_lines: Self::create_connection_lines(world, Color::Black)?, slots, white_start_slots, black_start_slots, }) } pub fn center(&self) -> Vector3 { self.center } pub fn white_start_slots(&self) -> &[Vector2; 9] { &self.white_start_slots } pub fn black_start_slots(&self) -> &[Vector2; 9] { &self.black_start_slots } pub fn slots(&self) -> &[[[BoardSlot; 3]; 3]; 3] { &self.slots } pub fn slots_mut(&mut self) -> &mut [[[BoardSlot; 3]; 3]; 3] { &mut self.slots } pub fn close_to_marker(&mut self, position: Vector2) -> Option<&mut BoardSlot> { const DISTANCE: f32 = 1.25; for outer in self.slots.iter_mut() { for inner in outer.iter_mut() { for slot in inner.iter_mut() { if *slot.state.lock().unwrap() != BoardSlotState::Invalid { if (slot.position - position).magnitude() <= DISTANCE { return Some(slot); } } } } } None } pub fn get_neighbours<'a>( slot: &BoardSlot, board: &'a [[[BoardSlot; 3]; 3]; 3], ) -> Vec<&'a BoardSlot> { let mut neighbours = Vec::new(); for i in 0..6 { // find free neighbour field if let Some(neighbour) = match i { 0 => { if slot.x > 0 { Some(&board[slot.x - 1][slot.y][slot.z]) } else { None } } 1 => { if slot.x < 2 { Some(&board[slot.x + 1][slot.y][slot.z]) } else { None } } 2 => { if slot.y > 0 { Some(&board[slot.x][slot.y - 1][slot.z]) } else { None } } 3 => { if slot.y < 2 { Some(&board[slot.x][slot.y + 1][slot.z]) } else { None } } 4 => { if slot.z > 0 { Some(&board[slot.x][slot.y][slot.z - 1]) } else { None } } 5 => { if slot.z < 2 { Some(&board[slot.x][slot.y][slot.z + 1]) } else { None } } _ => unreachable!(), } { let neighbour_state = neighbour.state.lock().unwrap(); match *neighbour_state { BoardSlotState::Empty => { neighbours.push(neighbour); } _ => (), } } } neighbours } } impl Board { fn create_board_base(world: &World) -> Result { let mut board_base = AssetMesh::new( world.resources.get::().device(), world .resources .get::() .graphics_info()? .render_type, )?; let vertex_buffer = Buffer::builder() .set_data(&Objects::create_cuboid( vec3(-22.5, -25.0, -2.0), vec3(45.0, 50.0, 2.0), )) .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .build(world.resources.get::().device().clone())?; let color = Color::try_from("#b77b2b")?; let a: [f32; 3] = color.into(); board_base.add_primitive( vertex_buffer, None, None, PrimitiveMaterial { color: [a[0], a[1], a[2], 1.0], metallic_factor: 0.2, emissive_factor: [0.5, 0.5, 0.5], roughness_factor: 0.8, alpha_mode: AlphaMode::Opaque, alpha_cut_off: 0.5, }, true, )?; Ok(board_base) } fn create_connection_lines(world: &mut World, color: Color) -> Result> { let mut entities = Vec::new(); let mut lines = AssetHandler::create(world).empty_entity(); let draw = Draw::new(vec![{ let mut mesh = AssetMesh::new( world.resources.get::().device(), world .resources .get::() .graphics_info()? .render_type, )?; let vertex_buffer = Buffer::builder() .set_data( &[ Objects::create_flat_quad([ vec2(-15.25, -15.25), vec2(15.25, -15.25), vec2(15.25, -14.75), vec2(-15.25, -14.75), ]), Objects::create_flat_quad([ vec2(-10.25, -10.25), vec2(10.25, -10.25), vec2(10.25, -9.75), vec2(-10.25, -9.75), ]), Objects::create_flat_quad([ vec2(-5.25, -5.25), vec2(5.25, -5.25), vec2(5.25, -4.75), vec2(-5.25, -4.75), ]), Objects::create_flat_quad([ vec2(-15.25, 14.75), vec2(15.25, 14.75), vec2(15.25, 15.25), vec2(-15.25, 15.25), ]), Objects::create_flat_quad([ vec2(-10.25, 9.75), vec2(10.25, 9.75), vec2(10.25, 10.25), vec2(-10.25, 10.25), ]), Objects::create_flat_quad([ vec2(-5.25, 4.75), vec2(5.25, 4.75), vec2(5.25, 5.25), vec2(-5.25, 5.25), ]), Objects::create_flat_quad([ vec2(-14.75, -15.25), vec2(-14.75, 15.25), vec2(-15.25, 15.25), vec2(-15.25, -15.25), ]), Objects::create_flat_quad([ vec2(-9.75, -10.25), vec2(-9.75, 10.25), vec2(-10.25, 10.25), vec2(-10.25, -10.25), ]), Objects::create_flat_quad([ vec2(-4.75, -5.25), vec2(-4.75, 5.25), vec2(-5.25, 5.25), vec2(-5.25, -5.25), ]), Objects::create_flat_quad([ vec2(15.25, -15.25), vec2(15.25, 15.25), vec2(14.75, 15.25), vec2(14.75, -15.25), ]), Objects::create_flat_quad([ vec2(10.25, -10.25), vec2(10.25, 10.25), vec2(9.75, 10.25), vec2(9.75, -10.25), ]), Objects::create_flat_quad([ vec2(5.25, -5.25), vec2(5.25, 5.25), vec2(4.75, 5.25), vec2(4.75, -5.25), ]), Objects::create_flat_quad([ vec2(0.25, -15.25), vec2(0.25, -4.75), vec2(-0.25, -4.75), vec2(-0.25, -15.25), ]), Objects::create_flat_quad([ vec2(-0.25, 15.25), vec2(-0.25, 4.75), vec2(0.25, 4.75), vec2(0.25, 15.25), ]), Objects::create_flat_quad([ vec2(-15.25, -0.25), vec2(-4.75, -0.25), vec2(-4.75, 0.25), vec2(-15.25, 0.25), ]), Objects::create_flat_quad([ vec2(15.25, 0.25), vec2(4.75, 0.25), vec2(4.75, -0.25), vec2(15.25, -0.25), ]), ] .concat(), ) .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) .set_memory_usage(MemoryUsage::CpuOnly) .build(world.resources.get::().device().clone())?; let a: [f32; 3] = color.into(); mesh.add_primitive( vertex_buffer, None, None, PrimitiveMaterial { color: [a[0], a[1], a[2], 1.0], metallic_factor: 0.2, emissive_factor: [0.2, 0.2, 0.2], roughness_factor: 0.8, alpha_mode: AlphaMode::Opaque, alpha_cut_off: 0.5, }, true, )?; mesh }]); lines.insert_component(draw); let mut location = Location::from_entity(&mut lines); location.set_offset(vec3(0.0, 0.0, MillGame::OFFSET_TO_BOARD)); lines.insert_component(location); entities.push(world.add_entity(lines)?); Ok(entities) } }