diff --git a/Cargo.lock b/Cargo.lock index 2129af5..0ed034d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + [[package]] name = "accesskit" version = "0.18.0" @@ -67,6 +83,19 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -389,7 +418,7 @@ dependencies = [ "bevy_utils", "blake3", "derive_more", - "downcast-rs", + "downcast-rs 2.0.1", "either", "petgraph", "ron", @@ -416,7 +445,7 @@ dependencies = [ "cfg-if", "console_error_panic_hook", "ctrlc", - "downcast-rs", + "downcast-rs 2.0.1", "log", "thiserror 2.0.12", "variadics_please", @@ -447,7 +476,7 @@ dependencies = [ "crossbeam-channel", "derive_more", "disqualified", - "downcast-rs", + "downcast-rs 2.0.1", "either", "futures-io", "futures-lite", @@ -993,7 +1022,7 @@ dependencies = [ "bevy_utils", "derive_more", "disqualified", - "downcast-rs", + "downcast-rs 2.0.1", "erased-serde", "foldhash", "glam", @@ -1049,7 +1078,7 @@ dependencies = [ "bytemuck", "codespan-reporting", "derive_more", - "downcast-rs", + "downcast-rs 2.0.1", "encase", "fixedbitset", "futures-lite", @@ -1530,6 +1559,18 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + [[package]] name = "cc" version = "1.2.22" @@ -1939,6 +1980,12 @@ dependencies = [ "rayon", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "downcast-rs" version = "2.0.1" @@ -3450,6 +3497,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owned_ttf_parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +dependencies = [ + "ttf-parser 0.25.1", +] + [[package]] name = "parking" version = "2.2.1" @@ -3675,6 +3731,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.40" @@ -4046,12 +4111,31 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "self_cell" version = "1.2.0" @@ -4169,6 +4253,31 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.9.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + [[package]] name = "smol_str" version = "0.2.2" @@ -4214,6 +4323,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + [[package]] name = "strum" version = "0.26.3" @@ -4387,6 +4502,31 @@ dependencies = [ "weezl", ] +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.9.0" @@ -4535,6 +4675,12 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + [[package]] name = "twox-hash" version = "2.1.0" @@ -4761,6 +4907,114 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wayland-backend" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +dependencies = [ + "cc", + "downcast-rs 1.2.1", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +dependencies = [ + "bitflags 2.9.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.9.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +dependencies = [ + "bitflags 2.9.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.77" @@ -5441,6 +5695,7 @@ version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0d05bd8908e14618c9609471db04007e644fd9cce6529756046cfc577f9155e" dependencies = [ + "ahash", "android-activity", "atomic-waker", "bitflags 2.9.0", @@ -5455,6 +5710,7 @@ dependencies = [ "dpi", "js-sys", "libc", + "memmap2", "ndk 0.9.0", "objc2", "objc2-app-kit", @@ -5466,11 +5722,17 @@ dependencies = [ "raw-window-handle", "redox_syscall 0.4.1", "rustix", + "sctk-adwaita", + "smithay-client-toolkit", "smol_str", "tracing", "unicode-segmentation", "wasm-bindgen", "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", "web-sys", "web-time", "windows-sys 0.52.0", @@ -5529,6 +5791,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +[[package]] +name = "xcursor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" + [[package]] name = "xkbcommon-dl" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index 1316378..7da3eeb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -bevy = "0.16.0" +bevy = { version = "0.16", features = ["wayland"] } noise = "0.9.0" rand = "0.9.1" image = "0.25.6" diff --git a/src/camera.rs b/src/camera.rs index a739306..16d2f58 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -75,7 +75,7 @@ pub fn spawn_panning_camera(mut commands: Commands) { scale: 1.0, ..OrthographicProjection::default_2d() }), - Transform::from_xyz(0., 0., 10. * TILE_SIZE), + Transform::from_xyz(0., 0., 10.), PanningCamera { pan_speed: 15.0 }, )); } diff --git a/src/citizen.rs b/src/citizen.rs index 5fd329e..d202c3d 100644 --- a/src/citizen.rs +++ b/src/citizen.rs @@ -145,14 +145,8 @@ pub fn citizen_movement( .par_iter_mut() .for_each(|(mut ambulatory, mut transform, _, _)| { let current_pos = transform.translation; - let below_pos = Vec3::new( - current_pos.x, - current_pos.y, - current_pos.z - TILE_SIZE - 0.1, - ); - if !is_standable_tile(&tilemap, current_pos.as_ivec3()) { - transform.translation = below_pos + Vec3::new(0.0, 0.0, 0.1); + transform.translation.z -= TILE_SIZE; return; } @@ -204,26 +198,32 @@ pub fn citizen_movement( } fn is_standable_tile(tilemap: &TileMap, pos: IVec3) -> bool { + let mut can_i_stand_in_tile: bool = false; + let mut can_i_stand_on_tile_bellow: bool = false; + let mut can_i_stand_in_fixture: bool = false; + let mut can_i_stand_on_fixture_bellow: bool = false; + // Check if current position has a blocking floor tile - if let Some(current_tile) = tilemap.floor_tiles.get(&pos) { - return current_tile.0 == 0; + if let Some(current_floor_tile) = tilemap.floor_tiles.get(&pos) { + can_i_stand_in_tile = current_floor_tile.1; } - // Check if current position has a solid fixture tile (e.g., log, leaf) - if let Some(current_tile) = tilemap.fixture_tiles.get(&pos) { - return current_tile.0 == 0; + // Check if current position has a solid fixture tile (e.g., log) + if let Some(current_fixture_tile) = tilemap.fixture_tiles.get(&pos) { + can_i_stand_in_fixture = current_fixture_tile.1; } // Check if there's solid ground below (fixture or floor) 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; + if let Some(below_floor_tile) = tilemap.floor_tiles.get(&pos_below) { + can_i_stand_on_tile_bellow = below_floor_tile.2; } - if let Some(below_tile) = tilemap.fixture_tiles.get(&pos_below) { - return below_tile.0 != 0; + if let Some(below_fixture_tile) = tilemap.fixture_tiles.get(&pos_below) { + can_i_stand_on_fixture_bellow = below_fixture_tile.2; } - false + return (can_i_stand_in_tile || can_i_stand_in_fixture) + && (can_i_stand_on_tile_bellow || can_i_stand_on_fixture_bellow); } fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { @@ -304,15 +304,43 @@ fn calculate_path(tilemap: &TileMap, start: IVec3, goal: IVec3) -> Vec { continue; } + // TODO: Add terrain-based cost modifiers + // movement_cost = apply_terrain_modifier(movement_cost, neighbor_pos, tilemap); + // Examples: + // - Mud/sand: +50% cost + // - Ice: +100% cost + // - Designated high-traffic areas: -25% cost + // - Designated restricted areas: +500% cost + // - Etc + let movement_cost = match ( move_dir.x.abs() / ITILE_SIZE, move_dir.y.abs() / ITILE_SIZE, move_dir.z.abs() / ITILE_SIZE, ) { - (1, 0, 0) | (0, 1, 0) | (0, 0, 1) => 10, // Orthogonal movement (1 axis) - (1, 1, 0) | (1, 0, 1) | (0, 1, 1) => 14, // Diagonal movement (2 axes) - (1, 1, 1) => 20, // Full 3D diagonal movement (3 axes) - _ => continue, // Invalid movement + // 2D Movement (Dwarf Fortress style) + (1, 0, 0) | (0, 1, 0) => 10, // Orthogonal movement + (1, 1, 0) => 14, // Diagonal movement (~√2 × 10) + + // Vertical Movement (Raw climbing - very expensive) + // (0, 0, 1) => 50, // Pure vertical climb/fall + + // 3D Movement (Climbing diagonally - even more expensive) + (1, 0, 1) | (0, 1, 1) => 52, // Orthogonal + vertical climb + (1, 1, 1) => 56, // Diagonal + vertical climb + + // TODO: Implement stairs and ramps for efficient vertical movement + // Stairs would reduce vertical costs significantly: + // (0, 0, 1) => 20 if has_stairs(current, neighbor_pos), // Stairs: 2× horizontal cost + // (1, 0, 1) | (0, 1, 1) => 24 if has_stairs(current, neighbor_pos), // Stairs + horizontal + // (1, 1, 1) => 28 if has_stairs(current, neighbor_pos), // Stairs + diagonal + + // TODO: Implement ramps for even smoother vertical movement + // Ramps would be cheaper than stairs: + // (0, 0, 1) => 15 if has_ramp(current, neighbor_pos), // Ramps: 1.5× horizontal cost + // (1, 0, 1) | (0, 1, 1) => 18 if has_ramp(current, neighbor_pos), // Ramps + horizontal + // (1, 1, 1) => 21 if has_ramp(current, neighbor_pos), // Ramps + diagonal + _ => continue, }; let new_g = g_scores.get(¤t).unwrap_or(&i32::MAX) + movement_cost; @@ -352,19 +380,35 @@ fn octile_distance_3d(a: IVec3, b: IVec3) -> i32 { let dy = (a.y - b.y).abs(); let dz = (a.z - b.z).abs(); - // Costs for movement along 1, 2, or 3 axes - let cost1 = 10; // Orthogonal - let cost2 = 14; // 2D Diagonal - let cost3 = 17; // 3D Diagonal + // Dwarf Fortress style costs + let cost_orthogonal = 10; // Horizontal orthogonal + let cost_diagonal = 14; // Horizontal diagonal (~√2 × 10) + let cost_climb = 50; // Raw vertical movement (climbing) let mut diffs = [dx, dy, dz]; - diffs.sort_unstable(); // Sorts ascending: [dmin, dmid, dmax] - + diffs.sort_unstable(); let dmin = diffs[0]; - let dmid = diffs[1]; let dmax = diffs[2]; - cost3 * dmin + cost2 * (dmid - dmin) + cost1 * (dmax - dmid) // Yikes.. + if dz == 0 { + // Pure 2D movement + let diagonal_moves = dmin / ITILE_SIZE; + let orthogonal_moves = (dmax - dmin) / ITILE_SIZE; + cost_diagonal * diagonal_moves + cost_orthogonal * orthogonal_moves + } else { + // Movement involves Z - assume raw climbing for now + // TODO: Modify this when stairs/ramps are implemented + let z_moves = dz / ITILE_SIZE; + let xy_distance = ((dx * dx + dy * dy) as f32).sqrt() as i32; + let remaining_2d_diagonal = (xy_distance.min(dz)) / ITILE_SIZE; + let remaining_2d_orthogonal = + (xy_distance - remaining_2d_diagonal * ITILE_SIZE) / ITILE_SIZE; + + // Raw climbing cost + remaining 2D movement + cost_climb * z_moves + + cost_diagonal * remaining_2d_diagonal + + cost_orthogonal * remaining_2d_orthogonal + } } fn reconstruct_path(came_from: HashMap, mut current: IVec3) -> Vec { @@ -391,13 +435,17 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res) { let mut rng = rand::rng(); // Spawn a handful of citizens - for _ in 0..1000 { - let x: f32 = rng.random_range(-35.0..35.0); - let y: f32 = rng.random_range(-35.0..35.0); - let mut position = Vec3::new(x.round(), y.round(), 35.0) * TILE_SIZE; - position.z += 0.1; - - let cit = commands.spawn(Citizen::new(&asset_server, position)).id(); + for _ in 0..100 { + let cit = commands + .spawn(Citizen::new( + &asset_server, + Vec3::new( + rng.random_range(-25.0f32..25.0f32).round(), + rng.random_range(-25.0f32..25.0f32).round(), + 35.0, + ) * TILE_SIZE, + )) + .id(); commands.entity(cit).insert(VisibleGameEntity); } } diff --git a/src/main.rs b/src/main.rs index 72049cd..65c30cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ fn main() { .insert_resource(tiles::CurrentWorldSpriteState { state: tiles::TerrainSpriteState::Inactive, }) - .insert_resource(camera::CameraMoved(true)) + .insert_resource(camera::CameraMoved(false)) .insert_resource(tiles::QuiltCache::default()) .add_systems(PreStartup, tiles::initialize_textures) .add_plugins( diff --git a/src/tile.rs b/src/tile.rs index 79143a8..89d0454 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -47,8 +47,8 @@ impl Default for FixtureTile { #[derive(Resource, Default, Clone)] pub struct TileMap { - pub floor_tiles: HashMap, //id, opaque, walkable, astar_weight, visible_range - pub fixture_tiles: HashMap, // id, solid, visible_range + pub floor_tiles: HashMap, //id, canStandIn, canStandOn, visiblyTransparent, astar_weight, visible_range + pub fixture_tiles: HashMap, // id, canStandIn, canStandOn, visible_range } pub fn update_tile_visibility( @@ -58,15 +58,12 @@ pub fn update_tile_visibility( ) { let now = Instant::now(); - let current_z = z_index.0 as isize; - for (terrain_sprite, mut visibility) in query.iter_mut() { - *visibility = - if terrain_sprite.z_index == ((current_z + tilemap::Z_BELOW as isize) as usize) { - Visibility::Visible - } else { - Visibility::Hidden - }; + *visibility = if terrain_sprite.z_index == ((z_index.0 + tilemap::Z_BELOW) as usize) { + Visibility::Visible + } else { + Visibility::Hidden + }; } cwss.state = TerrainSpriteState::Inactive; println!("Visibility update: {:.2?}", now.elapsed()); diff --git a/src/tilemap.rs b/src/tilemap.rs index f66d2fd..c0ba95f 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -15,9 +15,11 @@ use noise::{NoiseFn, Perlin}; pub const CHUNK_SIZE: i32 = 8; -pub const Z_BELOW: f32 = 10.0; +pub const Z_BELOW: f32 = 5.0; pub const Z_ABOVE: f32 = 15.0; -pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; // MAX 255 DO NOT EXCEED +pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; + +const _: () = assert!(Z_TOTAL <= 255.0); pub const SEED: u32 = 420; @@ -100,14 +102,14 @@ pub fn handle_tile_occlusion_updates( if let Some(visibility) = update_map.get(&pos.translation.as_ivec3()) { tile.visible_range = *visibility; if let Some(tile_data) = tilemap.floor_tiles.get_mut(&pos.translation.as_ivec3()) { - tile_data.4 = *visibility; + tile_data.5 = *visibility; } } } if count > 0 { cwss.state = TerrainSpriteState::WaitingForRender; println!( - "Tile occlusion updated {} tiles in {:.2?}", + "Tile occlusion calculated for {} tiles in {:.2?}", count, start.elapsed() ); @@ -131,8 +133,10 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { 'vertical_check: for z_offset in 1..v_check_height { 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 { + if let Some(&(_, _, _, visibly_transparent, _, _)) = + tilemap.floor_tiles.get(&above_pos) + { + if !visibly_transparent { is_occluded = true; break 'vertical_check; } @@ -154,8 +158,9 @@ pub fn calculate_visibility(pos: IVec3, tilemap: &TileMap) -> [u32; 8] { pos.z + z_offset * ITILE_SIZE, ); - if let Some(&(id, _, _, _, _)) = tilemap.floor_tiles.get(&neighbor_pos) { + if let Some(&(id, _, _, _, _, _)) = tilemap.floor_tiles.get(&neighbor_pos) { if id == 0 { + // id 0 = air tile is_visible = true; break 'neighbor_check; } @@ -188,7 +193,7 @@ pub fn generate_surface_terrain(x: i32, y: i32) -> f32 { (noise_value * 2.5) as f32 } -fn generate_chunks_from_algo( +fn handle_chunk_events( mut chunk_events: EventReader, mut terrain_event_writer: EventWriter, mut weathering_event_writer: EventWriter, @@ -255,7 +260,8 @@ fn generate_chunk_terrain( let start_y = chunk_pos.y * CHUNK_SIZE; let mut surface_positions: Vec<(Vec3, String)> = Vec::new(); - let mut local_tilemap_updates = HashMap::new(); + let mut local_tilemap_updates: HashMap = + HashMap::new(); // Generate tiles for this chunk for local_y in 0..CHUNK_SIZE { @@ -288,19 +294,22 @@ fn generate_chunk_terrain( commands.command_scope(|mut cmd| { FloorTilePrefab::air(position).spawn(&mut cmd); }); - local_tilemap_updates.insert(pos_ivec, (0, false, true, 0, [0; 8])); + local_tilemap_updates + .insert(pos_ivec, (0, true, false, true, 0, [0; 8])); // Air tile } else if cave_value < 0.8 { commands.command_scope(|mut cmd| { FloorTilePrefab::rock(position).spawn(&mut cmd); }); - local_tilemap_updates.insert(pos_ivec, (2, true, false, 50, [0; 8])); + local_tilemap_updates + .insert(pos_ivec, (2, false, true, false, 50, [0; 8])); // Rock tile } else { commands.command_scope(|mut cmd| { FloorTilePrefab::dirt(position).spawn(&mut cmd); }); - local_tilemap_updates.insert(pos_ivec, (1, true, true, 85, [0; 8])); + local_tilemap_updates + .insert(pos_ivec, (1, false, true, false, 85, [0; 8])); // Dirt tile } } else if noise_position.z > position.z { @@ -310,20 +319,22 @@ fn generate_chunk_terrain( commands.command_scope(|mut cmd| { FloorTilePrefab::grass(position).spawn(&mut cmd); }); - local_tilemap_updates.insert(pos_ivec, (1, true, true, 100, [0; 8])); // Dirt tile (grass) + local_tilemap_updates + .insert(pos_ivec, (1, false, true, false, 100, [0; 8])); // Dirt tile (grass) surface_positions.push((position, ("grass").to_string())); } else { commands.command_scope(|mut cmd| { FloorTilePrefab::dirt(position).spawn(&mut cmd); }); - local_tilemap_updates.insert(pos_ivec, (1, true, true, 85, [0; 8])); + local_tilemap_updates + .insert(pos_ivec, (1, false, true, false, 85, [0; 8])); // Dirt tile } } else { commands.command_scope(|mut cmd| { FloorTilePrefab::air(position).spawn(&mut cmd); }); - local_tilemap_updates.insert(pos_ivec, (0, false, true, 0, [0; 8])); + local_tilemap_updates.insert(pos_ivec, (0, true, false, true, 0, [0; 8])); // Air tile } } @@ -387,16 +398,16 @@ fn generate_chunk_forrestry( let start = Instant::now(); let count = events.len(); - let collected_tilemap_updates = Mutex::new(Vec::<(IVec3, (u32, bool, [u32; 8]))>::new()); + let collected_tilemap_updates: Mutex> = + Mutex::new(Vec::<(IVec3, (i32, bool, bool, [u32; 8]))>::new()); events.par_read().for_each(|event| { let floor_positions = &event.floor_tiles; - let tree_positions: Vec = Vec::new(); + let mut tree_positions: Vec = Vec::new(); let min_distance = 7.0 * TILE_SIZE; for (position, floor_type) in floor_positions.iter() { let above_pos = *position + Vec3::new(0.0, 0.0, TILE_SIZE); - let above_ivec = above_pos.as_ivec3(); match floor_type.as_str() { "grass" => { @@ -409,10 +420,10 @@ fn generate_chunk_forrestry( .iter() .all(|&tree_pos| above_pos.distance(tree_pos) > min_distance); - // 1 in 65 chance if far enough from other trees - if is_far_enough && rand::random::() % 65 == 0 { + // 1 in 100 chance if far enough from other trees + if is_far_enough && rand::random::() % 100 == 0 { // Generate trunk - let trunk_height = 3 + rand::random::() % 4; + let trunk_height = 4 + rand::random::() % 5; for i in 0..trunk_height { let trunk_pos = above_pos + Vec3::new(0.0, 0.0, i as f32 * TILE_SIZE); @@ -423,36 +434,34 @@ fn generate_chunk_forrestry( continue; } - FixtureTilePrefab::log(trunk_pos).spawn(&mut commands); + let trunk_entity = + FixtureTilePrefab::log(trunk_pos).spawn(&mut commands); + tree_positions.push(trunk_pos); + // Add sprite component to the same entity if texture exists if let Some(texture_id) = texture_ids.refs.get(&500004) { if let Some(texture) = textures.handles.get(texture_id) { let sprite = Sprite { image: texture.clone(), ..Default::default() }; - let log = commands - .spawn(( - sprite, - Transform::from_xyz( - trunk_pos.x, - trunk_pos.y, - trunk_pos.z, - ), - )) - .id(); - commands.entity(log).insert(VisibleGameEntity); - collected_tilemap_updates - .lock() - .unwrap() - .push((trunk_ivec, (1, true, [0; 8]))); + + commands + .entity(trunk_entity) + .insert((sprite, VisibleGameEntity)); + // .insert(Visibility::Visible); // Override the hidden visibility } } + collected_tilemap_updates + .lock() + .unwrap() + .push((trunk_ivec, (1, false, true, [0; 8]))); + log_positions.insert(trunk_ivec); } // Generate leaves - 3D spherical canopy with random shape - let base_leaf_radius = 3.0; + let base_leaf_radius = 2.25; let leaf_center = above_pos + Vec3::new(0.0, 0.0, trunk_height as f32 * TILE_SIZE); @@ -478,7 +487,7 @@ fn generate_chunk_forrestry( let y_f = y as f32; let z_f = z as f32; let radius = base_leaf_radius - * (1.0 + (rand::random::() * 0.2 - 0.1)); + * (1.0 + (rand::random::() * 0.35 - 0.1)); if x_f * x_f + y_f * y_f + z_f * z_f <= radius * radius { FixtureTilePrefab::leaves(pos).spawn(&mut commands); @@ -503,7 +512,7 @@ fn generate_chunk_forrestry( collected_tilemap_updates .lock() .unwrap() - .push((ivec, (5, true, [1; 8]))); + .push((ivec, (5, false, true, [0; 8]))); } } } @@ -512,11 +521,6 @@ fn generate_chunk_forrestry( } } }); - - collected_tilemap_updates - .lock() - .unwrap() - .push((above_ivec, (1, true, [0; 8]))); } _ => {} } @@ -554,7 +558,7 @@ fn generate_chunk_fauna( fn setup_initial_chunks(mut event_writer: EventWriter) { for x in -10..=10 { - for y in -10..=10 { + for y in -5..=5 { event_writer.write(GenerateChunkEvent { chunk_position: IVec2::new(x, y), }); @@ -562,22 +566,6 @@ fn setup_initial_chunks(mut event_writer: EventWriter) { } } -pub fn index_z_to_absolute_z(z: f32) -> f32 { - z + Z_BELOW -} - -pub fn world_z_to_absolute_z(z: f32) -> f32 { - z + Z_BELOW * ITILE_SIZE as f32 -} - -pub fn absolute_z_to_index_z(z: f32) -> f32 { - z - Z_BELOW -} - -pub fn absolute_z_to_world_z(z: f32) -> f32 { - z - Z_BELOW * ITILE_SIZE as f32 -} - #[derive(Component)] pub struct VisibleGameEntity; @@ -588,13 +576,22 @@ pub fn compute_visibility_of_game_entities( query .par_iter_mut() .for_each(|(transform, mut visibility, mut sprite)| { - // if too far away, saturation does not matter - if (z_index.0 - transform.translation.z / TILE_SIZE) / 8.0 > 1.0 { + let entity_z = (transform.translation.z / TILE_SIZE) - 1.0; + // if same z-level, always visible + if z_index.0 == entity_z { + sprite.color = Color::WHITE; + sprite.color.set_alpha(1.0); + *visibility = Visibility::Visible; + return; + }; + + // if entity too low, saturation does not matter + if z_index.0 - entity_z > 8.0 { *visibility = Visibility::Hidden; return; } - // if too high, saturation does not matter - if (transform.translation.z) / TILE_SIZE > z_index.0 + 1.0 { + // if entity too high, saturation does not matter + if entity_z > z_index.0 { *visibility = Visibility::Hidden; return; } @@ -626,7 +623,7 @@ impl Plugin for TilemapPlugin { .add_systems( FixedUpdate, ( - generate_chunks_from_algo, + handle_chunk_events, generate_chunk_terrain, generate_chunk_weathering_and_precipitation, generate_chunk_forrestry, diff --git a/src/tiles.rs b/src/tiles.rs index 5843625..b9de973 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -1,4 +1,5 @@ use crate::tile::{FixtureTile, FloorTile, TileState}; +use crate::tilemap::Z_BELOW; use crate::{constants::*, tilemap}; use bevy::asset::RenderAssetUsages; use bevy::prelude::*; @@ -33,6 +34,9 @@ const LOG_PATH: &str = "log.png"; const LEAVES_PATH: &str = "leaves.png"; pub fn initialize_textures(mut commands: Commands, asset_server: Res) { + println!(""); + println!("####################"); + let start = Instant::now(); let mut textures: HashMap> = HashMap::new(); let mut texture_ids: HashMap = HashMap::new(); texture_ids.insert(u32::MAX, DEFAULT_TEXTURE.to_string()); @@ -82,6 +86,7 @@ pub fn initialize_textures(mut commands: Commands, asset_server: Res