diff --git a/assets/bedrock.png b/assets/bedrock_floor.png similarity index 100% rename from assets/bedrock.png rename to assets/bedrock_floor.png diff --git a/assets/bedrock_wall.png b/assets/bedrock_wall.png new file mode 100644 index 0000000..ce302bb Binary files /dev/null and b/assets/bedrock_wall.png differ diff --git a/assets/dirt.png b/assets/dirt_floor.png similarity index 100% rename from assets/dirt.png rename to assets/dirt_floor.png diff --git a/assets/dirt_wall.png b/assets/dirt_wall.png new file mode 100644 index 0000000..f757f51 Binary files /dev/null and b/assets/dirt_wall.png differ diff --git a/assets/grass.png b/assets/grass_floor.png similarity index 100% rename from assets/grass.png rename to assets/grass_floor.png diff --git a/assets/grass_wall.png b/assets/grass_wall.png new file mode 100644 index 0000000..9bd0821 Binary files /dev/null and b/assets/grass_wall.png differ diff --git a/assets/natural walls/rock/end.png b/assets/natural walls/rock/end.png new file mode 100644 index 0000000..8ac74de Binary files /dev/null and b/assets/natural walls/rock/end.png differ diff --git a/assets/natural walls/rock/inner_a.png b/assets/natural walls/rock/inner_a.png new file mode 100644 index 0000000..5e7b1d5 Binary files /dev/null and b/assets/natural walls/rock/inner_a.png differ diff --git a/assets/natural walls/rock/inner_ab.png b/assets/natural walls/rock/inner_ab.png new file mode 100644 index 0000000..982b18e Binary files /dev/null and b/assets/natural walls/rock/inner_ab.png differ diff --git a/assets/natural walls/rock/inner_abc.png b/assets/natural walls/rock/inner_abc.png new file mode 100644 index 0000000..e8da18c Binary files /dev/null and b/assets/natural walls/rock/inner_abc.png differ diff --git a/assets/natural walls/rock/inner_abcd.png b/assets/natural walls/rock/inner_abcd.png new file mode 100644 index 0000000..35a7afe Binary files /dev/null and b/assets/natural walls/rock/inner_abcd.png differ diff --git a/assets/natural walls/rock/inner_ac.png b/assets/natural walls/rock/inner_ac.png new file mode 100644 index 0000000..c3c61db Binary files /dev/null and b/assets/natural walls/rock/inner_ac.png differ diff --git a/assets/natural walls/rock/line.png b/assets/natural walls/rock/line.png new file mode 100644 index 0000000..1a07042 Binary files /dev/null and b/assets/natural walls/rock/line.png differ diff --git a/assets/natural walls/rock/line_with_a.png b/assets/natural walls/rock/line_with_a.png new file mode 100644 index 0000000..84d0a09 Binary files /dev/null and b/assets/natural walls/rock/line_with_a.png differ diff --git a/assets/natural walls/rock/line_with_ab.png b/assets/natural walls/rock/line_with_ab.png new file mode 100644 index 0000000..47c3173 Binary files /dev/null and b/assets/natural walls/rock/line_with_ab.png differ diff --git a/assets/natural walls/rock/line_with_b.png b/assets/natural walls/rock/line_with_b.png new file mode 100644 index 0000000..2be37ad Binary files /dev/null and b/assets/natural walls/rock/line_with_b.png differ diff --git a/assets/natural walls/rock/outer.png b/assets/natural walls/rock/outer.png new file mode 100644 index 0000000..7850345 Binary files /dev/null and b/assets/natural walls/rock/outer.png differ diff --git a/assets/rock.png b/assets/rock_floor.png similarity index 100% rename from assets/rock.png rename to assets/rock_floor.png diff --git a/assets/rock_wall.png b/assets/rock_wall.png new file mode 100644 index 0000000..566aedd Binary files /dev/null and b/assets/rock_wall.png differ diff --git a/assets/underground.png b/assets/underground.png deleted file mode 100644 index 802e47d..0000000 Binary files a/assets/underground.png and /dev/null differ diff --git a/src/game.rs b/src/game.rs index bb80338..ca34edc 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,9 +1,7 @@ -use std::process::exit; - -use crate::citizen::Citizen; use crate::constants::*; -use crate::item::{Item, ItemBundle}; -use crate::tiles::TilePrefab; +use crate::item::ItemBundle; +use crate::tiles::FloorTilePrefab; +use crate::{citizen::Citizen, tiles::FixtureTilePrefab}; use bevy::prelude::*; use noise::{NoiseFn, Perlin}; use rand::prelude::*; @@ -28,17 +26,14 @@ pub fn setup_level(mut commands: Commands, asset_server: Res) { fn setup_tilemap(commands: &mut Commands, asset_server: &Res) { let noise = Perlin::new(0); + let mut floor_positions = Vec::new(); + // First pass: Generate and store floor tiles for y in -50..50 { for x in -50..50 { - // Base noise for terrain height let noise_value_a = noise.get([x as f64 * 0.01, y as f64 * 0.01]) * 1.25; - // Additional noise for bumpiness let noise_value_b = noise.get([x as f64 * 0.05, y as f64 * 0.05]) * 0.25; - - // Combine the two noise values let combined_noise_value = noise_value_a + noise_value_b; - // print!("({}, {}, {}), ", noise_value_a, noise_value_b, combined_noise_value); let noise_position = Vec3::new( (x as f32 * TILE_SIZE).round(), @@ -54,70 +49,125 @@ fn setup_tilemap(commands: &mut Commands, asset_server: &Res) { ); let items: Vec = vec![]; + let mut floor_type = None; if noise_position.z > position.z { if z < -15 { let noise_value = - noise.get([x as f64 * 0.1, y as f64 * 0.1, z as f64 * 0.1]); + noise.get([x as f64 * 0.05, y as f64 * 0.05, z as f64 * 0.05]); if noise_value < -0.5 { commands - .spawn(TilePrefab::air(position, asset_server)) + .spawn(FloorTilePrefab::air(position, asset_server)) .with_children(|parent| { for item in items { parent.spawn(item); } }); + floor_type = Some("air"); } else if noise_value < 0.8 { commands - .spawn(TilePrefab::rock(position, asset_server)) + .spawn(FloorTilePrefab::rock(position, asset_server)) .with_children(|parent| { for item in items { parent.spawn(item); } }); + floor_type = Some("rock"); } else { commands - .spawn(TilePrefab::dirt(position, asset_server)) + .spawn(FloorTilePrefab::dirt(position, asset_server)) .with_children(|parent| { for item in items { parent.spawn(item); } }); + floor_type = Some("dirt"); } } else { commands - .spawn(TilePrefab::dirt(position, asset_server)) + .spawn(FloorTilePrefab::dirt(position, asset_server)) .with_children(|parent| { for item in items { parent.spawn(item); } }); + floor_type = Some("dirt"); } } else if noise_position.z < position.z { commands - .spawn(TilePrefab::air(position, asset_server)) + .spawn(FloorTilePrefab::air(position, asset_server)) .with_children(|parent| { for item in items { parent.spawn(item); } }); + floor_type = Some("air"); } else { commands - .spawn(TilePrefab::grass(position, asset_server)) + .spawn(FloorTilePrefab::dirt(position, asset_server)) .with_children(|parent| { for item in items { parent.spawn(item); } }); + floor_type = Some("dirt"); + } + + // Store floor type and position + if let Some(floor_type) = floor_type { + floor_positions.push((position, floor_type)); } } } } + // Generate bedrock floor for x in -50..50 { for y in -50..50 { let position = Vec3::new(x as f32 * TILE_SIZE, y as f32 * TILE_SIZE, -150.); - commands.spawn(TilePrefab::bedrock(position, asset_server)); + commands.spawn(FloorTilePrefab::bedrock(position, asset_server)); + floor_positions.push((position, "bedrock")); + } + } + + // Sort floor positions by z coordinate (highest to lowest) to process from top to bottom + floor_positions.sort_by(|a, b| b.0.z.partial_cmp(&a.0.z).unwrap()); + + // Create a map to easily look up floor types at specific positions + let floor_map: std::collections::HashMap<(i32, i32, i32), &str> = floor_positions + .iter() + .map(|(pos, floor_type)| ((pos.x as i32, pos.y as i32, pos.z as i32), *floor_type)) + .collect(); + + // Second pass: Generate fixtures based on floor tiles above + for (position, _) in floor_positions.iter() { + let x = position.x as i32; + let y = position.y as i32; + let z = position.z as i32; + + // Look up the floor type one z-level above + if let Some(above_floor_type) = floor_map.get(&(x, y, z + TILE_SIZE as i32)) { + // Skip if the floor above is air or if we're at the surface + if *above_floor_type == "air" { + continue; + } + + // Calculate fixture position (one tile above current floor) + let fixture_position = Vec3::new(position.x, position.y, position.z + 1.); + + // Spawn appropriate fixture based on floor type above + match *above_floor_type { + "dirt" => { + commands.spawn(FixtureTilePrefab::dirt(fixture_position, asset_server)); + } + "rock" => { + commands.spawn(FixtureTilePrefab::rock(fixture_position, asset_server)); + } + "bedrock" => { + commands.spawn(FixtureTilePrefab::bedrock(fixture_position, asset_server)); + } + _ => {} + } } } } diff --git a/src/main.rs b/src/main.rs index 9423538..406117c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,7 @@ fn main() { ) .init_resource::() .add_systems( - FixedUpdate, + Update, ( tile::camera_z_movement, tile::update_tile_visibility diff --git a/src/tile.rs b/src/tile.rs index 23a08fa..f676d41 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -4,62 +4,44 @@ use crate::item::Item; use bevy::ecs::system::ParamSet; use bevy::prelude::*; use std::collections::HashMap; -use std::process::exit; #[derive(Component, Clone)] #[require(Sprite)] -pub struct Tile { +pub struct FloorTile { pub id: u32, pub opaque: bool, - pub visible_range: [u32; 8], -} - -impl Default for Tile { - fn default() -> Self { - Self { - id: 1, - opaque: true, - visible_range: [0; 8], - } - } -} - -#[derive(Component)] -#[require(Tile)] -pub struct FloorTile { pub walkable: bool, pub astar_weight: u8, + pub visible_range: [u32; 8], } impl Default for FloorTile { fn default() -> Self { Self { + id: 0, + opaque: true, walkable: true, - astar_weight: 1, + astar_weight: 0, + visible_range: [0; 8], } } } -#[derive(Component)] -pub struct WallTile { - pub tile: Tile, +#[derive(Component, Clone)] +pub struct FixtureTile { + pub id: u32, pub solid: bool, - pub embrasure: bool, // Arrowslit, crenelle, grate, cage etc + pub visible_range: [u32; 8], } -#[derive(Component)] -#[require(Tile)] -pub struct HasWater { - pub height: u8, - pub astarmultiplier: f32, - pub swimmable: bool, -} - -#[derive(Component)] -pub struct DoorTile { - pub tile: Tile, - pub open: bool, - pub locked: bool, +impl Default for FixtureTile { + fn default() -> Self { + Self { + id: 0, + solid: true, + visible_range: [0; 8], + } + } } #[derive(Resource, Default)] @@ -76,63 +58,61 @@ pub fn camera_z_movement( z_index.0 -= 1.0; z_index.0 = z_index.0.clamp(-149.0, 5.0); camera_moved.0 = true; + println!("z_index = {}", z_index.0); } if keyboard_input.just_pressed(KeyCode::ShiftRight) { z_index.0 += 1.0; z_index.0 = z_index.0.clamp(-149.0, 5.0); camera_moved.0 = true; + println!("z_index = {}", z_index.0); } } pub fn tile_sprite_generate_occlusion_map( - mut query_set: ParamSet<(Query<(&Transform, &Tile)>, Query<(&mut Tile, &Transform)>)>, + mut query_set: ParamSet<( + Query<(&Transform, &FloorTile)>, + Query<(&Transform, &FixtureTile)>, + Query<(&mut FloorTile, &Transform)>, + Query<(&mut FixtureTile, &Transform)>, + )>, ) { - // First, create the tile map using only the first query + // Create the tile map using floor query let tile_map: HashMap<(i32, i32, i32), (bool, u32)> = { - let first_query = query_set.p0(); - first_query - .iter() - .map(|(transform, tile)| { + let mut map = HashMap::new(); + + // Add floor tiles + query_set.p0().iter().for_each(|(transform, tile)| { + map.insert( ( - ( - transform.translation.x as i32, - transform.translation.y as i32, - transform.translation.z as i32, - ), - (tile.opaque, tile.id), - ) - }) - .collect() - }; - - // Then, process tiles using the second query and our collected tile_map - let mut second_query = query_set.p1(); - second_query - .par_iter_mut() - .for_each(|(mut tile, transform)| { - // Clear existing visible range - tile.visible_range = [0; 8]; - - let pos = ( - transform.translation.x as i32, - transform.translation.y as i32, - transform.translation.z as i32, + transform.translation.x as i32, + transform.translation.y as i32, + transform.translation.z as i32, + ), + (tile.opaque, tile.id), // false indicates floor ); + }); + map + }; + let tile_size = TILE_SIZE as i32; + + // Helper function to calculate visibility + let calculate_visibility = + |pos: (i32, i32, i32), tile_map: &HashMap<(i32, i32, i32), (bool, u32)>| { + let mut visible_range = [0u32; 8]; - // Check visibility for each possible z-index for mut camera_z in -150..100 { - camera_z *= TILE_SIZE as i32; + camera_z *= tile_size; let mut is_visible = false; - // Skip if tile is above camera - if pos.2 > (camera_z) { + if pos.2 > camera_z { continue; } - // Check for opaque tiles above let mut is_occluded = false; - 'vertical_check: for z_offset in 1..=20 { - let above_pos = (pos.0, pos.1, pos.2 + (z_offset * TILE_SIZE as i32)); + + 'vertical_check: for z_offset in 1..=35 { + // check above + let above_pos = (pos.0, pos.1, pos.2 + (z_offset * tile_size)); if above_pos.2 <= camera_z { if let Some(&(is_opaque, _)) = tile_map.get(&above_pos) { if is_opaque { @@ -146,22 +126,20 @@ pub fn tile_sprite_generate_occlusion_map( } if !is_occluded { - // Check neighbors for visibility + let base_pos = (pos.0, pos.1, pos.2); 'neighbor_check: for x_offset in -1..=1 { for y_offset in -1..=1 { for z_offset in 0..=1 { if x_offset == 0 && y_offset == 0 && z_offset == 0 { continue; } - let neighbor_pos = ( - pos.0 + (x_offset * TILE_SIZE as i32), - pos.1 + (y_offset * TILE_SIZE as i32), - pos.2 + (z_offset * TILE_SIZE as i32), + base_pos.0 + x_offset * tile_size, + base_pos.1 + y_offset * tile_size, + base_pos.2 + z_offset * tile_size, ); if let Some(&(_, id)) = tile_map.get(&neighbor_pos) { if id == 0 { - // air tile is_visible = true; break 'neighbor_check; } @@ -172,158 +150,81 @@ pub fn tile_sprite_generate_occlusion_map( } if is_visible { - let z2 = ((camera_z / TILE_SIZE as i32) + 150) as usize; - tile.visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32); + let z2 = ((camera_z / tile_size) + 150) as usize; + visible_range[z2 / 32] |= 1 << ((z2 % 32) as u32); } } + + visible_range + }; + + // Update floor tiles + query_set + .p2() + .par_iter_mut() + .for_each(|(mut tile, transform)| { + let pos = ( + transform.translation.x as i32, + transform.translation.y as i32, + transform.translation.z as i32, + ); + tile.visible_range = calculate_visibility(pos, &tile_map); + }); + + // Update fixture tiles + query_set + .p3() + .par_iter_mut() + .for_each(|(mut fixture, transform)| { + let pos = ( + transform.translation.x as i32, + transform.translation.y as i32, + transform.translation.z as i32 - 1, + ); + fixture.visible_range = calculate_visibility(pos, &tile_map); }); } pub fn update_tile_visibility( z_index: ResMut, - mut query: Query<(&Tile, &mut Visibility)>, -) { - let z_index = (z_index.0 as i32 + 150) as usize; - query.par_iter_mut().for_each(|(tile, mut visibility)| { - let is_visible = (tile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0; - *visibility = if is_visible { - Visibility::Visible - } else { - Visibility::Hidden - }; - }); -} - -// TODO CHECK THIS -pub fn update_occlusion_map_for_tile( - updated_pos: Vec3, mut query_set: ParamSet<( - Query<(&Transform, &Tile)>, - Query<(&mut Visibility, &mut Tile, &Transform)>, + Query<(&FloorTile, &mut Visibility)>, + Query<(&FixtureTile, &mut Visibility)>, )>, ) { - use std::collections::HashMap; + let z_index = (z_index.0 as i32 + 150) as usize; - // Convert the updated position into integer coordinates - let updated_pos = ( - updated_pos.x as i32, - updated_pos.y as i32, - updated_pos.z as i32, - ); + // Update floor visibility + query_set + .p0() + .par_iter_mut() + .for_each(|(tile, mut visibility)| { + let is_visible = (tile.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0; + *visibility = if is_visible { + Visibility::Visible + } else { + Visibility::Hidden + }; + }); - // Build a tile map as before, but only for relevant tiles - let tile_map: HashMap<(i32, i32, i32), (bool, u32)> = { - let first_query = query_set.p0(); - first_query - .iter() - .map(|(transform, tile)| { - ( - ( - transform.translation.x as i32, - transform.translation.y as i32, - transform.translation.z as i32, - ), - (tile.opaque, tile.id), - ) - }) - .collect() - }; - - // Get the second query for mutable access to tiles - let mut second_query = query_set.p1(); - - // Iterate over a 3×3 area centered around the updated tile - for x_offset in -1..=1 { - for y_offset in -1..=1 { - for z_offset in -1..=1 { - let current_pos = ( - updated_pos.0 + x_offset, - updated_pos.1 + y_offset, - updated_pos.2 + z_offset, - ); - - // Find the tile at the current position - if let Some((mut visibility, mut tile, _)) = - second_query.iter_mut().find(|(_, _, transform)| { - let pos = ( - transform.translation.x as i32, - transform.translation.y as i32, - transform.translation.z as i32, - ); - pos == current_pos - }) - { - // Clear the visible range for the current tile - tile.visible_range = [0; 8]; - - // Recalculate visibility for each possible z-index - for camera_z in current_pos.2 - 20..=current_pos.1 + 1 { - let mut is_visible = false; - - // Skip if the tile is above the camera - if current_pos.2 > camera_z { - continue; - } - - // Check for occluding tiles above - let mut is_occluded = false; - for z_offset in 1..=20 { - let above_pos = - (current_pos.0, current_pos.1, current_pos.2 + z_offset); - if above_pos.2 <= camera_z { - if let Some(&(is_opaque, _)) = tile_map.get(&above_pos) { - if is_opaque { - is_occluded = true; - break; - } - } - } - } - - if !is_occluded { - // Check neighbors for visibility - 'neighbor_check: for x_offset in -1..=1 { - for y_offset in -1..=1 { - for z_offset in 0..=1 { - if x_offset == 0 && y_offset == 0 && z_offset == 0 { - continue; - } - - let neighbor_pos = ( - current_pos.0 + x_offset, - current_pos.1 + y_offset, - current_pos.2 + z_offset, - ); - - if let Some(&(_, id)) = tile_map.get(&neighbor_pos) { - if id == 0 { - // sky tile - is_visible = true; - break 'neighbor_check; - } - } - } - } - } - } - - if is_visible { - tile.visible_range[((camera_z + 150) / 32) as usize] |= - 1 << (((camera_z + 150) % 32) as u32); - } - } - - // Update visibility state - *visibility = Visibility::Hidden; - } - } - } - } + // Update fixture visibility + query_set + .p1() + .par_iter_mut() + .for_each(|(fixture, mut visibility)| { + let is_visible = + (fixture.visible_range[z_index / 32] & (1 << (z_index % 32) as u32)) != 0; + *visibility = if is_visible { + Visibility::Visible + } else { + Visibility::Hidden + }; + }); } pub fn tile_item_sprite_update( time: Res