diff --git a/src/citizen.rs b/src/citizen.rs index e3a1ef8..df01a6b 100644 --- a/src/citizen.rs +++ b/src/citizen.rs @@ -3,7 +3,8 @@ use crate::{ tile, tilemap::{generate_surface_noise, ChunkMap, CHUNK_SIZE}, }; -use bevy::prelude::*; +use bevy::{math::ivec3, prelude::*}; +use noise::Negate; #[derive(Bundle)] pub struct Citizen { @@ -46,7 +47,7 @@ pub struct Ambulatory { pub target: Option, } -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Debug)] struct PathNode { position: IVec3, f_score: i32, @@ -103,9 +104,9 @@ pub fn update_citizen_wandering_targets( // Find a valid z-level near the surface for z in (surface_height - 3)..=(surface_height + 4) { - let mut target_pos = IVec3::new(target_x, target_y, z) * TILE_SIZE as i32; + let mut target_pos = IVec3::new(target_x, target_y, z) * ITILE_SIZE; if let Some(floor_tile) = tilemap.floor_tiles.get(&target_pos) { - target_pos.z += TILE_SIZE as i32; + target_pos.z += ITILE_SIZE; if floor_tile.2 { if let Some(floor_tile_above) = tilemap.floor_tiles.get(&target_pos) { if floor_tile_above.0 == 0 { @@ -150,22 +151,18 @@ pub fn citizen_movement( } if let Some(target) = ambulatory.target { - // Convert world position to tile coordinates - let current_tile = IVec3::new( - (transform.translation.x / TILE_SIZE).floor() as i32, - (transform.translation.y / TILE_SIZE).floor() as i32, - (transform.translation.z / TILE_SIZE).floor() as i32, - ); - - let target_tile = IVec3::new( - (target.x / TILE_SIZE).floor() as i32, - (target.y / TILE_SIZE).floor() as i32, - (target.z / TILE_SIZE).floor() as i32, - ); - // Calculate path if needed if ambulatory.current_path.is_none() { - ambulatory.current_path = Some(calculate_path(&tilemap, current_tile, target_tile)); + println!( + "from {:?} to {:?}", + transform.translation.as_ivec3(), + target.as_ivec3() - ivec3(0, 0, 1) + ); + ambulatory.current_path = Some(calculate_path( + &tilemap, + transform.translation.as_ivec3(), + target.as_ivec3() - ivec3(0, 0, 1), + )); ambulatory.path_index = 0; } @@ -174,9 +171,9 @@ pub fn citizen_movement( if ambulatory.path_index < path.len() { let next_point = path[ambulatory.path_index]; let next_world_pos = Vec3::new( - next_point.x * TILE_SIZE + TILE_SIZE / 2.0, - next_point.y * TILE_SIZE + TILE_SIZE / 2.0, - next_point.z * TILE_SIZE + TILE_SIZE / 2.0, + next_point.x + TILE_SIZE / 2.0, + next_point.y + TILE_SIZE / 2.0, + next_point.z + TILE_SIZE / 2.0, ); let direction = (next_world_pos - transform.translation).normalize(); @@ -206,7 +203,7 @@ fn is_valid_move(tilemap: &TileMap, pos: IVec3) -> bool { } // Check if there's solid ground below - let pos_below = pos - IVec3::new(0, 0, 1); + let pos_below = pos - IVec3::new(0, 0, ITILE_SIZE); if let Some(below_tile) = tilemap.floor_tiles.get(&pos_below) { return below_tile.0 != 0; } @@ -231,19 +228,20 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { // Define allowed movements: orthogonal + diagonal on same level, and up/down let allowed_moves = vec![ // Cardinal directions (same level) - IVec3::new(1, 0, 0), - IVec3::new(-1, 0, 0), - IVec3::new(0, 1, 0), - IVec3::new(0, -1, 0), + IVec3::new(ITILE_SIZE, 0, 0), + IVec3::new(-ITILE_SIZE, 0, 0), + IVec3::new(0, ITILE_SIZE, 0), + IVec3::new(0, -ITILE_SIZE, 0), // Diagonal directions (same level) - IVec3::new(1, 1, 0), - IVec3::new(1, -1, 0), - IVec3::new(-1, 1, 0), - IVec3::new(-1, -1, 0), + IVec3::new(ITILE_SIZE, ITILE_SIZE, 0), + IVec3::new(ITILE_SIZE, -ITILE_SIZE, 0), + IVec3::new(-ITILE_SIZE, ITILE_SIZE, 0), + IVec3::new(-ITILE_SIZE, -ITILE_SIZE, 0), ]; while let Some(current) = open_set.pop() { if current.position == goal { + println!("path found"); return reconstruct_path(came_from, current.position); } @@ -254,13 +252,13 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { let mut neighbor_pos = current.position + *move_dir; // Try one up if same level is invalid - let up_pos = neighbor_pos + IVec3::new(0, 0, 1); + let up_pos = neighbor_pos + IVec3::new(0, 0, ITILE_SIZE); if !is_valid_move(tilemap, neighbor_pos) && is_valid_move(tilemap, up_pos) { neighbor_pos = up_pos; } // Try one down if same level is invalid - let down_pos = neighbor_pos - IVec3::new(0, 0, 1); + let down_pos = neighbor_pos - IVec3::new(0, 0, ITILE_SIZE); if !is_valid_move(tilemap, neighbor_pos) && is_valid_move(tilemap, down_pos) { neighbor_pos = down_pos; } @@ -269,15 +267,20 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { continue; } + + let current_level = ITILE_SIZE; + let upper = 2*ITILE_SIZE; + let lower = -ITILE_SIZE; + // Calculate movement cost let movement_cost = match ( move_dir.x.abs() + move_dir.y.abs(), neighbor_pos.z - current.position.z, ) { - (1, 0) => 10, // Orthogonal movement - (2, 0) => 14, // Diagonal movement - (_, 1) => 20, // Moving up one level - (_, -1) => 15, // Moving down one level + (current_level, 0) => 10, // Orthogonal movement + (upper, 0) => 14, // Diagonal movement + (_, current_level) => 20, // Moving up one level + (_, lower) => 15, // Moving down one level _ => continue, // Invalid movement }; @@ -298,6 +301,7 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { } } + println!("No path found"); // If no path found, return direct line vec![ Vec3::new(start.x as f32, start.y as f32, start.z as f32), @@ -341,7 +345,7 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res) { let mut rng = rand::thread_rng(); // Spawn a handful of citizens - for _ in 0..10 { + for _ in 0..1 { let x: f32 = rng.gen_range(-15.0..10.0); let y: f32 = rng.gen_range(-10.0..10.0); let mut position = Vec3::new(x.round(), y.round(), 5.0) * TILE_SIZE; diff --git a/src/constants.rs b/src/constants.rs index 8246d63..f351df6 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,2 +1,3 @@ pub const PIXEL_RATIO: f32 = 2.0; pub const TILE_SIZE: f32 = 16.0 * PIXEL_RATIO; +pub const ITILE_SIZE: i32 = TILE_SIZE as i32; diff --git a/src/main.rs b/src/main.rs index c1f0b69..30c3130 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,6 +29,7 @@ fn main() { .add_plugins(FrameTimeDiagnosticsPlugin::default()) // Add the FrameTimeDiagnosticsPlugin here .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(tilemap::TilemapPlugin) + .add_plugins(PathfindingPlugin) .add_systems( Startup, ( @@ -41,8 +42,6 @@ fn main() { FixedUpdate, ( camera::camera_movement, - citizen::update_citizen_wandering_targets, - citizen::citizen_movement, cursor::move_cursor, // citizen::check_citizen_positions_for_chunks, ), @@ -54,13 +53,14 @@ fn main() { tile::camera_z_movement, tile::update_tile_visibility .run_if(|camera_moved: Res| camera_moved.0), - log_fps_system, + // log_fps_system, ), ) .run(); } use bevy::diagnostic::DiagnosticsStore; +use citizen::PathfindingPlugin; fn log_fps_system(diagnostics: Res) { if let Some(fps) = diagnostics diff --git a/src/tilemap.rs b/src/tilemap.rs index 8a04be4..e51aea2 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -1,4 +1,4 @@ -use crate::constants::TILE_SIZE; +use crate::constants::{ITILE_SIZE, TILE_SIZE}; use crate::tile::{self, FixtureTile, FloorTile, NeedsOccluded, TileMap}; use crate::tiles::{FixtureTilePrefab, FloorTilePrefab}; use bevy::prelude::*; @@ -72,12 +72,10 @@ pub fn handle_tile_occlusion_updates( } pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { - let tile_size = TILE_SIZE as i32; - let mut visible_range = [0u32; 8]; for mut camera_z in -15..=5 { - camera_z *= tile_size; + camera_z *= ITILE_SIZE; let mut is_visible = false; if pos.z > camera_z { @@ -87,7 +85,7 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { let mut is_occluded = false; 'vertical_check: for z_offset in 1..5 { - let above_pos = IVec3::new(pos.x, pos.y, pos.z + (z_offset * tile_size)); + let above_pos = IVec3::new(pos.x, pos.y, pos.z + (z_offset * ITILE_SIZE)); if above_pos.z <= camera_z { if let Some(&(_, opaque, _, _, _)) = tilemap.floor_tiles.get(&above_pos) { if opaque { @@ -117,9 +115,9 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { continue; } let neighbor_pos = IVec3::new( - pos.x + x_offset * tile_size, - pos.y + y_offset * tile_size, - pos.z + z_offset * tile_size, + pos.x + x_offset * ITILE_SIZE, + pos.y + y_offset * ITILE_SIZE, + pos.z + z_offset * ITILE_SIZE, ); if let Some(&(id, _, _, _, _)) = tilemap.floor_tiles.get(&neighbor_pos) { @@ -140,7 +138,7 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { } if is_visible { - let z2 = ((camera_z / tile_size) + 150) as usize; + let z2 = ((camera_z / ITILE_SIZE) + 150) as usize; visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32); } }