From d2dd70caa097f48c2e11f4d70b653f79ce7baf0e Mon Sep 17 00:00:00 2001 From: popertots Date: Fri, 23 May 2025 17:09:58 +0100 Subject: [PATCH] add trees --- assets/leaves.png | Bin 0 -> 817 bytes src/citizen.rs | 23 +++++--- src/tilemap.rs | 132 +++++++++++++++++++++++++++++++++++++--------- src/tiles.rs | 16 +++++- 4 files changed, 137 insertions(+), 34 deletions(-) create mode 100644 assets/leaves.png diff --git a/assets/leaves.png b/assets/leaves.png new file mode 100644 index 0000000000000000000000000000000000000000..4dfb9d8819bad135364252c172d2e63ac1c3d015 GIT binary patch literal 817 zcmV-11J3-3P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk1s4w&EwizoVgLXF zheD2w^Q(E*={Ib0 z^I6=l3k(A>rXQAKX3TD%vk(FwKYSKUDAt-Doh4Qjo^;SK;eV=myjym$yod-I3~><0 zj>+{xn#a54hrgCz0DdA!-!;Ynoxz^PrRc>JG#Indlb8ytmCsw<+Iai-txJGrcc&}> zho_0DdE$~I6p8^Z610$fcK4X2{rz!%^C`WGM0!7blFp!E;;bU=e%&+}W=6c_a=#)KXNU-@ zaDTgyR-3=H-R;NY}s z*~NbON3+`fx&d_hiM7ugc6j>Q1IXb?-PDvQa)>j;xf~;%T+byPG%N%~9QYzI8!e^T z-5EdJJ!fy92paOp#RPA;i(F-LHP3tzHv92D bool { - // Check if the tile at current position is not blocking + // Check if current position has a blocking floor tile if let Some(current_tile) = tilemap.floor_tiles.get(&pos) { - if current_tile.0 != 0 { - return false; - } + return current_tile.0 == 0; + } + // 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 there's solid ground below + // 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_tile) = tilemap.fixture_tiles.get(&pos_below) { + return below_tile.0 != 0; + } false } @@ -384,9 +391,9 @@ pub fn spawn_citizens(mut commands: Commands, asset_server: Res) { let mut rng = rand::rng(); // Spawn a handful of citizens - for _ in 0..100 { - let x: f32 = rng.random_range(-5.0..5.0); - let y: f32 = rng.random_range(-5.0..5.0); + 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; diff --git a/src/tilemap.rs b/src/tilemap.rs index 56a3f87..f66d2fd 100644 --- a/src/tilemap.rs +++ b/src/tilemap.rs @@ -10,12 +10,13 @@ use crate::tiles::{ use crate::{camera, game}; use bevy::prelude::*; use bevy_platform::collections::hash_map::HashMap; +use bevy_platform::collections::HashSet; use noise::{NoiseFn, Perlin}; pub const CHUNK_SIZE: i32 = 8; pub const Z_BELOW: f32 = 10.0; -pub const Z_ABOVE: f32 = 8.0; +pub const Z_ABOVE: f32 = 15.0; pub const Z_TOTAL: f32 = Z_ABOVE + Z_BELOW; // MAX 255 DO NOT EXCEED pub const SEED: u32 = 420; @@ -390,7 +391,7 @@ fn generate_chunk_forrestry( events.par_read().for_each(|event| { let floor_positions = &event.floor_tiles; - let mut tree_positions: Vec = Vec::new(); + let tree_positions: Vec = Vec::new(); let min_distance = 7.0 * TILE_SIZE; for (position, floor_type) in floor_positions.iter() { @@ -400,32 +401,113 @@ fn generate_chunk_forrestry( match floor_type.as_str() { "grass" => { commands.command_scope(|mut commands| { + // Track log positions to avoid leaf overlap + let mut log_positions = HashSet::new(); + // Check if position is far enough from existing trees let is_far_enough = tree_positions .iter() .all(|&tree_pos| above_pos.distance(tree_pos) > min_distance); - // 1 in 25 chance if far enough from other trees - if is_far_enough && rand::random::() % 45 == 0 { - FixtureTilePrefab::log(above_pos).spawn(&mut commands); - 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( - above_pos.x, - above_pos.y, - above_pos.z, - ), - )) - .id(); - commands.entity(log).insert(VisibleGameEntity); - tree_positions.push(above_pos); + // 1 in 65 chance if far enough from other trees + if is_far_enough && rand::random::() % 65 == 0 { + // Generate trunk + let trunk_height = 3 + rand::random::() % 4; + for i in 0..trunk_height { + let trunk_pos = + above_pos + Vec3::new(0.0, 0.0, i as f32 * TILE_SIZE); + let trunk_ivec = trunk_pos.as_ivec3(); + + // Skip if position already has a tile + if tilemap.fixture_tiles.get(&trunk_ivec).is_some() { + continue; + } + + FixtureTilePrefab::log(trunk_pos).spawn(&mut commands); + 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]))); + } + } + + log_positions.insert(trunk_ivec); + } + + // Generate leaves - 3D spherical canopy with random shape + let base_leaf_radius = 3.0; + let leaf_center = + above_pos + Vec3::new(0.0, 0.0, trunk_height as f32 * TILE_SIZE); + + for x in -base_leaf_radius as i32..=base_leaf_radius as i32 { + for y in -base_leaf_radius as i32..=base_leaf_radius as i32 { + for z in -base_leaf_radius as i32..=base_leaf_radius as i32 { + let pos = Vec3::new( + x as f32 * TILE_SIZE, + y as f32 * TILE_SIZE, + z as f32 * TILE_SIZE, + ) + leaf_center; + let ivec = pos.as_ivec3(); + + // Skip if position already has a tile or is a log + if tilemap.fixture_tiles.get(&ivec).is_some() + || log_positions.contains(&ivec) + { + continue; + } + + // Apply random radius variation for natural shape + let x_f = x as f32; + 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)); + + if x_f * x_f + y_f * y_f + z_f * z_f <= radius * radius { + FixtureTilePrefab::leaves(pos).spawn(&mut commands); + if let Some(texture_id) = texture_ids.refs.get(&500005) + { + if let Some(texture) = + textures.handles.get(texture_id) + { + let sprite = Sprite { + image: texture.clone(), + ..Default::default() + }; + let leaf = commands + .spawn(( + sprite, + Transform::from_xyz( + pos.x, pos.y, pos.z, + ), + )) + .id(); + commands.entity(leaf).insert(VisibleGameEntity); + collected_tilemap_updates + .lock() + .unwrap() + .push((ivec, (5, true, [1; 8]))); + } + } + } + } } } } @@ -471,8 +553,8 @@ fn generate_chunk_fauna( } fn setup_initial_chunks(mut event_writer: EventWriter) { - for x in -8..=8 { - for y in -5..=5 { + for x in -10..=10 { + for y in -10..=10 { event_writer.write(GenerateChunkEvent { chunk_position: IVec2::new(x, y), }); diff --git a/src/tiles.rs b/src/tiles.rs index fc637ba..5843625 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -30,6 +30,7 @@ const DIRT_WALL_PATH: &str = "dirt_wall.png"; const ROCK_WALL_PATH: &str = "rock_wall.png"; const BEDROCK_WALL_PATH: &str = "bedrock_wall.png"; const LOG_PATH: &str = "log.png"; +const LEAVES_PATH: &str = "leaves.png"; pub fn initialize_textures(mut commands: Commands, asset_server: Res) { let mut textures: HashMap> = HashMap::new(); @@ -76,6 +77,8 @@ pub fn initialize_textures(mut commands: Commands, asset_server: Res Self { + FixtureTilePrefab { + transform: Transform::from_translation(position), + tile: FixtureTile { + id: FIXTURE_ID_OFFSET + 5, ..Default::default() }, visibility: Visibility::Hidden,