From 17a88acd1c39b54156c870d75d1b8a0ea7a506bb Mon Sep 17 00:00:00 2001 From: Syniis Date: Wed, 21 Feb 2024 22:39:32 +0100 Subject: [PATCH] Various tweaks. New ArtLeaves BlockKind --- assets/world/features.ron | 20 +- common/net/src/msg/compression.rs | 5 + common/src/terrain/block.rs | 3 + voxygen/src/hud/minimap.rs | 6 +- world/src/layer/cave.rs | 546 ++++++++++++++++++------------ world/src/lib.rs | 4 +- 6 files changed, 346 insertions(+), 238 deletions(-) diff --git a/assets/world/features.ron b/assets/world/features.ron index b60a5f2014..53a2053d77 100644 --- a/assets/world/features.ron +++ b/assets/world/features.ron @@ -3,17 +3,17 @@ ( caverns: false, // TODO: Disabled by default until cave overhaul - caves: false, - rocks: false, - shrubs: false, - trees: false, + caves: true, + rocks: true, + shrubs: true, + trees: true, scatter: true, - paths: false, - spots: false, - site2_towns: false, + paths: true, + spots: true, + site2_towns: true, site2_giant_trees: true, - wildlife_density: 0.0, - peak_naming: false, - biome_naming: false, + wildlife_density: 1.0, + peak_naming: true, + biome_naming: true, train_tracks: false, // TODO: train stations, train entities ) diff --git a/common/net/src/msg/compression.rs b/common/net/src/msg/compression.rs index 8428ca416c..beed5715a0 100644 --- a/common/net/src/msg/compression.rs +++ b/common/net/src/msg/compression.rs @@ -671,6 +671,11 @@ impl VoxelImageDecoding for TriPngEncoding Rgb { + r: 93, + g: 206, + b: 64, + }, GlowingMushroom => Rgb { r: 50, g: 250, diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 35a2d3f8f3..d89cad8c6b 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -55,6 +55,7 @@ make_case_elim!( Leaves = 0x41, GlowingMushroom = 0x42, Ice = 0x43, + ArtLeaves = 0x44, // 0x43 <= x < 0x50 is reserved for future tree parts // Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we // often want to experiment with new kinds of block without allocating them a @@ -451,6 +452,7 @@ impl Block { match self.kind() { BlockKind::Water => (0, 0.4), BlockKind::Leaves => (9, 255.0), + BlockKind::ArtLeaves => (9, 255.0), BlockKind::Wood => (6, 2.0), BlockKind::Snow => (6, 2.0), BlockKind::ArtSnow => (6, 2.0), @@ -488,6 +490,7 @@ impl Block { // so all is good for empty fluids. match self.kind() { BlockKind::Leaves => Some(0.25), + BlockKind::ArtLeaves => Some(0.25), BlockKind::Grass => Some(0.5), BlockKind::WeakRock => Some(0.75), BlockKind::Snow => Some(0.1), diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 32b101145d..2b300b01c1 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -137,7 +137,11 @@ impl VoxelMinimap { // since otherwise trees would cause ceiling removal to trigger // when running under a branch. let is_filled = block.map_or(true, |b| { - b.is_filled() && !matches!(b.kind(), BlockKind::Leaves | BlockKind::Wood) + b.is_filled() + && !matches!( + b.kind(), + BlockKind::Leaves | BlockKind::ArtLeaves | BlockKind::Wood + ) }); let rgba = rgba.unwrap_or_else(|| Rgba::new(0, 0, 0, 255)); (rgba, is_filled) diff --git a/world/src/layer/cave.rs b/world/src/layer/cave.rs index e0350aef41..858cf55399 100644 --- a/world/src/layer/cave.rs +++ b/world/src/layer/cave.rs @@ -26,6 +26,10 @@ use std::{ }; use vek::*; +fn close_fast(x: f32, tgt: f32, falloff: f32, falloff_strength: i32) -> f32 { + (1.0 - ((x - tgt) / falloff).powi(falloff_strength * 2)).max(0.0) +} + const CELL_SIZE: i32 = 1536; #[derive(Copy, Clone)] @@ -43,6 +47,7 @@ fn to_wpos(cell: Vec2, level: u32) -> Vec2 { const AVG_LEVEL_DEPTH: i32 = 120; const LAYERS: u32 = 4; +const MIN_RADIUS: f32 = 8.0; const MAX_RADIUS: f32 = 64.0; fn node_at(cell: Vec2, level: u32, land: &Land) -> Option { @@ -89,8 +94,6 @@ pub struct Tunnel { } impl Tunnel { - const RADIUS_RANGE: Range = 8.0..MAX_RADIUS as f64; - fn ctrl_offset(&self) -> Vec2 { let start = self.a.wpos.map(|e| e as f64 + 0.5); let end = self.b.wpos.map(|e| e as f64 + 0.5); @@ -108,7 +111,7 @@ impl Tunnel { Vec2::new(start, end), ) { let dist2 = closest.distance_squared(wposf); - if dist2 < (Self::RADIUS_RANGE.end + threshold).powi(2) { + if dist2 < (MAX_RADIUS as f64 + threshold).powi(2) { Some((t, closest, dist2.sqrt())) } else { None @@ -128,8 +131,8 @@ impl Tunnel { let _end = self.b.wpos.map(|e| e as f64 + 0.5); if let Some((t, closest, dist)) = self.possibly_near(wposf, 1.0) { let horizontal = Lerp::lerp_unclamped( - Self::RADIUS_RANGE.start, - Self::RADIUS_RANGE.end, + MIN_RADIUS as f64, + MAX_RADIUS as f64, (info.index().noise.cave_fbm_nz.get( (closest.with_z(info.land().get_alt_approx(self.a.wpos) as f64) / 256.0) .into_array(), @@ -138,8 +141,8 @@ impl Tunnel { .powf(3.0), ); let vertical = Lerp::lerp_unclamped( - Self::RADIUS_RANGE.start, - Self::RADIUS_RANGE.end, + MIN_RADIUS as f64, + MAX_RADIUS as f64, (info.index().noise.cave_fbm_nz.get( (closest.with_z(info.land().get_alt_approx(self.b.wpos) as f64) / 256.0) .into_array(), @@ -536,7 +539,7 @@ struct Flower { petals: usize, petal_height: f32, petal_radius: f32, - rotation: Mat3, + // rotation: Mat3, } #[inline_tweak::tweak_fn] @@ -569,23 +572,24 @@ fn write_column( .sub(0.5) .max(0.0) .mul(2.0) - .powi(if biome.icy > 0.5 {3} else {1}) + .powi(if biome.icy > 0.7 {2} else {1}) // No stalactites near entrances .mul(((col.alt - z_range.end as f32) / 32.0).clamped(0.0, 1.0)) // Prevent stalactites from blocking cave .mul(((cave_width + max_height) / 40.0).clamped(0.0, 1.0)) - .mul(8.0 + cavern_height * (0.4 + (biome.sandy - 0.5).max((biome.icy - 0.7) * 2.0).max(0.0))) + // Scale with cavern height, sandy and icy biomes have bigger stalactites + .mul(8.0 + cavern_height * (0.4 + (biome.sandy - 0.5).max(biome.icy - 0.6).max(0.0))) }; - let ceiling_cover = if (biome.leafy - 0.3) - .max(biome.mushroom - 0.5) - .max(biome.icy - 0.7) - .max(biome.sandy - 0.5) - .max(biome.fire - 0.5) - > 0.0 + let ceiling_cover = if biome.leafy > 0.3 + || biome.mushroom > 0.5 + || biome.icy > 0.7 + || biome.sandy > 0.5 + || biome.fire > 0.5 { + // 1.0 because at some point we maybe want to use some noise value here instead 1.0.mul(((col.alt - z_range.end as f32) / 32.0).clamped(0.0, 1.0)) - .mul(cavern_height * (dist_cave_center / cave_width).powf(3.33)) + .mul(max_height * (dist_cave_center / cave_width).powf(3.33)) .max(1.0) .sub( if col.marble_mid @@ -596,7 +600,7 @@ fn write_column( .max(biome.leafy - 0.4) .max(biome.mushroom - 0.4) { - cavern_height * col.marble_mid + max_height * col.marble_mid } else { 0.0 }, @@ -612,6 +616,7 @@ fn write_column( .mul(1.25) .sub(0.5) .max(0.0) + .mul(((cave_width + max_height) / 32.0).clamped(0.0, 1.0)) .mul(6.0 + cavern_height * 0.5) .mul(biome.fire) } else { @@ -633,8 +638,8 @@ fn write_column( 0.0 }; - let height_factor = (max_height / 32.0).clamped(0.0, 1.0).powf(2.0); - let width_factor = (cave_width / 32.0).clamped(0.0, 1.0).powf(2.0); + let height_factor = (max_height / MAX_RADIUS * 0.5).clamped(0.0, 1.0).powf(2.0); + let width_factor = (cave_width / MAX_RADIUS * 0.5).clamped(0.0, 1.0).powf(2.0); let ridge = FastNoise2d::new(38) .get(wpos2d.map(|e| e as f64 / 512.0)) .sub(0.25) @@ -652,32 +657,95 @@ fn write_column( .get(wpos2d.map(|e| e as f64 / 4.0)) .mul(1.15) .add(1.0) - .mul(0.25) + .mul(0.5) .mul(((col.alt - z_range.end as f32) / 16.0).clamped(0.0, 1.0)) .mul({ let (val, total) = [ - (biome.sandy - 0.3, 0.9), - (biome.dusty - 0.2, 0.5), - (biome.leafy - 0.5, 0.6), - (biome.barren - 0.1, 0.6), + (biome.sandy, 0.9), + (biome.dusty, 0.5), + (biome.leafy, 0.6), + (biome.barren, 0.6), ] .into_iter() .fold((0.0, 0.0), |a, x| (a.0 + x.0.max(0.0) * x.1, a.1 + x.1)); val / total }) - .mul(cavern_height * 0.4); + .mul(cavern_height * 0.2) + .clamped(0.0, 4.0); let rand = RandomField::new(37 + level); let is_ice = biome.icy + col.marble * 0.2 > 0.5 && col.marble > 0.6; let is_snow = biome.snowy + col.marble_mid * 0.2 > 0.5 && col.marble_mid > 0.6; - let dirt = 1 + (!is_ice) as i32 + is_snow as i32; + let (has_stalagmite, has_stalactite) = if biome.icy > 0.5 { + (col.marble_mid > 0.6, col.marble_mid < 0.5) + } else { + (true, true) + }; + + let stalagmite_only = if has_stalagmite && !has_stalactite { + 0.6f32 + } else { + 0.0f32 + }; + + // Don't cover stalgmites in ice biome with surface block + let dirt = if biome.icy > 0.5 && has_stalagmite && stalactite > 3.0 { + 0 + } else { + 1 + (!is_ice) as i32 + is_snow as i32 + }; let bedrock = z_range.start + lava as i32; let ridge_bedrock = bedrock + (ridge * 0.7) as i32; - let base = ridge_bedrock + (stalactite * (0.4 - biome.icy).max(0.0)) as i32; + let base = ridge_bedrock + (stalactite * (0.4 + stalagmite_only)) as i32; let floor = base + dirt + (ridge * 0.3) as i32 + bump as i32; - let ceiling = z_range.end - stalactite.max(ceiling_cover) as i32; + let ceiling = + z_range.end - (stalactite * has_stalactite as i32 as f32).max(ceiling_cover) as i32; + + let get_ceiling_drip = |wpos: Vec2, freq: f64, length: f32| { + let wposf = wpos.map(|e| e as f32); + let wposf = wposf + wposf.yx() * 1.1; + let dims = Vec2::new(11.0, 32.0); + let posf = wposf + Vec2::unit_y() * (wposf.x / dims.x).floor() * 89.0 / dims; + let pos = posf.map(|e| e.floor() as i32); + if rand.chance(pos.with_z(73), freq as f32) { + let drip_length = ((posf.y.fract() - 0.5).abs() * 2.0 * dims.y) + .mul(length * 0.01) + .powf(2.0) + .min(length); + + if (posf.x.fract() * 2.0 - 1.0).powi(2) < 1.0 { + drip_length + } else { + 0.0 + } + } else { + 0.0 + } + }; + + let ceiling_drip = ceiling + - if !void_above && !sky_above { + let c = if biome.mushroom > 0.9 && ceiling_cover > 0.0 { + Some((0.07, 7.0)) + } else if biome.icy > 0.9 { + Some((0.1 * col.marble_mid as f64, 9.0)) + } else { + None + }; + if let Some((freq, length)) = c { + get_ceiling_drip(wpos2d, freq, length).max(get_ceiling_drip( + wpos2d.yx(), + freq, + length, + )) as i32 + } else { + 0 + } + } else { + 0 + }; let mut get_structure = |wpos: Vec3, dynamic_rng: &mut R| { for (wpos2d, seed) in StructureGen2d::new(34537, 24, 8).get(wpos.xy()) { @@ -696,120 +764,125 @@ fn write_column( } if biome.mushroom > 0.7 - && rng.gen_bool( - 0.5 * close(vertical, MAX_RADIUS, 48.0) as f64 - * close(biome.mushroom, 1.0, 0.7) as f64, - ) - { - let purp = rng.gen_range(0..50); - Some(CaveStructure::Mushroom(Mushroom { - pos, - stalk: 8.0 - + rng.gen::().powf(2.0) - * (z_range.end - z_range.start - 8) as f32 - * 0.75, - head_color: Rgb::new( - 40 + purp, - rng.gen_range(60..120), - rng.gen_range(80..200) + purp, - ), - })) - } else if biome.crystal > 0.5 - && rng.gen_bool(0.4 * close(biome.crystal, 1.0, 0.7) as f64) - { - let on_ground = rng.gen_bool(0.6); - let pos = wpos2d.with_z(if on_ground { - z_range.start - } else { - z_range.end - }); - - let mut crystals: Vec = Vec::new(); - let max_length = (48.0 * close(vertical, MAX_RADIUS, 42.0)).max(12.0); - let length = rng.gen_range(8.0..max_length); - let radius = - Lerp::lerp(2.0, 4.5, length / max_length + rng.gen_range(-0.1..0.1)); - let dir = Vec3::new( - rng.gen_range(-3.0..3.0), - rng.gen_range(-3.0..3.0), - rng.gen_range(0.5..10.0) * if on_ground { 1.0 } else { -1.0 }, - ) - .normalized(); - - crystals.push(Crystal { - dir, - length, - radius, - }); - - (0..4).for_each(|_| { - crystals.push(Crystal { - dir: Vec3::new( - rng.gen_range(-1.0..1.0), - rng.gen_range(-1.0..1.0), - (dir.z + rng.gen_range(-0.2..0.2)).clamped(0.0, 1.0), + && vertical > 16.0 + && rng.gen_bool( + 0.5 * close_fast(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2) as f64 + * close_fast(biome.mushroom, 1.0, 0.7, 1) as f64, + ) + { + let purp = rng.gen_range(0..50); + Some(CaveStructure::Mushroom(Mushroom { + pos, + stalk: 8.0 + + rng.gen::().powf(2.0) + * (z_range.end - z_range.start - 8) as f32 + * 0.75, + head_color: Rgb::new( + 40 + purp, + rng.gen_range(60..120), + rng.gen_range(80..200) + purp, ), - length: length * rng.gen_range(0.3..0.8), - radius: (radius * rng.gen_range(0.5..0.8)).max(1.0), + })) + } else if biome.crystal > 0.5 + && rng.gen_bool(0.4 * close_fast(biome.crystal, 1.0, 0.7, 2) as f64) + { + let on_ground = rng.gen_bool(0.6); + let pos = wpos2d.with_z(if on_ground { + z_range.start + } else { + z_range.end }); - }); - let purple = rng.gen_range(25..75); - let blue = (rng.gen_range(45.0..75.0) * biome.icy) as u8; + let mut crystals: Vec = Vec::new(); + let max_length = + (48.0 * close_fast(vertical, MAX_RADIUS, MAX_RADIUS, 1)).max(12.0); + let length = rng.gen_range(8.0..max_length); + let radius = Lerp::lerp( + 2.0, + 4.5, + length / max_length + rng.gen_range(-0.1..0.1), + ); + let dir = Vec3::new( + rng.gen_range(-3.0..3.0), + rng.gen_range(-3.0..3.0), + rng.gen_range(0.5..10.0) * if on_ground { 1.0 } else { -1.0 }, + ) + .normalized(); - Some(CaveStructure::Crystal(CrystalCluster { - pos, - crystals, - color: Rgb::new( - 255 - blue * 2, - 255 - blue - purple, - 200 + rng.gen_range(25..55), - ), - })) - } else if biome.leafy > 0.8 - && rng.gen_bool( - 0.2 * (close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0) - * close(horizontal, MAX_RADIUS, MAX_RADIUS - 12.0) - * close(biome.leafy, 1.0, 0.2)) - as f64, - ) - { - Some(CaveStructure::Flower(Flower { - pos, - stalk: 4.0 - + rng.gen::().powf(2.0) - * (z_range.end - z_range.start - 8) as f32 - * 0.75, - petals: rng.gen_range(1..5) * 2 + 1, - petal_height: rng.gen_range(4.0..16.0), - petal_radius: rng.gen_range(8.0..16.0), - rotation: (Mat3::rotation_x( - -(rng.gen_bool(1.0) as u32 as f32) * std::f32::consts::PI - / rng.gen_range(3.0..16.0), - ) * Mat3::rotation_y( - std::f32::consts::PI / rng.gen_range(3.0..16.0), - )) - .transposed(), - })) - } else if (biome.leafy > 0.8 || giant_tree_factor > 0.0) - && rng.gen_bool( - (0.4 * close(biome.leafy, 1.0, 0.4).max(1.0 + giant_tree_factor) - as f64) - .clamped(0.0, 1.0), - ) - { - Some(CaveStructure::GiantRoot { - pos, - radius: rng.gen_range( - 2.0..(6.0 - + close(vertical, MAX_RADIUS, MAX_RADIUS / 2.0) * 2.0 - + close(horizontal, MAX_RADIUS, MAX_RADIUS / 2.0) * 2.0), - ), - height: (z_range.end - z_range.start) as f32, - }) - } else { - None - } + crystals.push(Crystal { + dir, + length, + radius, + }); + + (0..4).for_each(|_| { + crystals.push(Crystal { + dir: Vec3::new( + rng.gen_range(-1.0..1.0), + rng.gen_range(-1.0..1.0), + (dir.z + rng.gen_range(-0.2..0.2)).clamped(0.0, 1.0), + ), + length: length * rng.gen_range(0.3..0.8), + radius: (radius * rng.gen_range(0.5..0.8)).max(1.0), + }); + }); + + let purple = rng.gen_range(25..75); + let blue = (rng.gen_range(45.0..75.0) * biome.icy) as u8; + + Some(CaveStructure::Crystal(CrystalCluster { + pos, + crystals, + color: Rgb::new( + 255 - blue * 2, + 255 - blue - purple, + 200 + rng.gen_range(25..55), + ), + })) + } else if biome.leafy > 0.8 + && vertical > 16.0 + && horizontal > 8.0 + && rng.gen_bool( + 0.25 * (close_fast(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2) + * close_fast(horizontal, MAX_RADIUS, MAX_RADIUS - 8.0, 2) + * biome.leafy) as f64, + ) + { + let petal_radius = rng.gen_range(8.0..16.0); + Some(CaveStructure::Flower(Flower { + pos, + stalk: 6.0 + + rng.gen::().powf(2.0) + * (z_range.end - z_range.start - 8) as f32 + * 0.75, + petals: rng.gen_range(1..5) * 2 + 1, + petal_height: 0.4 + * petal_radius + * (1.0 + rng.gen::().powf(2.0)), + petal_radius, + })) + } else if (biome.leafy > 0.7 || giant_tree_factor > 0.0) + && rng.gen_bool( + (0.5 * close_fast(biome.leafy, 1.0, 0.5, 1) + .max(1.0 + giant_tree_factor) + as f64) + .clamped(0.0, 1.0), + ) + { + Some(CaveStructure::GiantRoot { + pos, + radius: rng.gen_range( + 1.5..(3.5 + + close_fast(vertical, MAX_RADIUS, MAX_RADIUS / 2.0, 2) + * 3.0 + + close_fast(horizontal, MAX_RADIUS, MAX_RADIUS / 2.0, 2) + * 3.0), + ), + height: (z_range.end - z_range.start) as f32, + }) + } else { + None + } }) { structure } else { @@ -868,8 +941,8 @@ fn write_column( block_kind, Rgb::new( 30, - 120 + (radial * 30.0) as u8, - 180 - (radial * 30.0) as u8, + 120 + (radial * 40.0) as u8, + 180 - (radial * 40.0) as u8, ), )); } else if head_dist < 1.0 { @@ -949,14 +1022,28 @@ fn write_column( } }, CaveStructure::Flower(flower) => { - let wposf = wpos.map(|e| e as f32); - - let rpos = wposf - flower.pos.map(|e| e as f32); + let wposf = wpos.map(|e| e as f64); + let warp_freq = 1.0 / 16.0; + let warp_amp = Vec3::new(8.0, 8.0, 8.0); + let xy = wposf.xy(); + let xz = Vec2::new(wposf.x, wposf.z); + let yz = Vec2::new(wposf.y, wposf.z); + let wposf_warped = wposf.map(|e| e as f32) + + Vec3::new( + FastNoise2d::new(seed).get(yz * warp_freq), + FastNoise2d::new(seed).get(xz * warp_freq), + FastNoise2d::new(seed).get(xy * warp_freq), + ) * warp_amp + * (wposf.z as f32 - flower.pos.z as f32) + .mul(1.0 / flower.stalk) + .sub(1.0) + .min(0.0) + .abs() + .clamped(0.0, 1.0); + let rpos = wposf_warped - flower.pos.map(|e| e as f32); let stalk_radius = 2.5f32; - let petal_radius = flower.petal_radius; let petal_thickness = 2.5; - let dist_sq = rpos.xy().magnitude_squared(); if rpos.z < flower.stalk && dist_sq @@ -967,52 +1054,49 @@ fn write_column( return Some(Block::new(BlockKind::Wood, Rgb::new(0, 108, 0))); } - let rpos = wposf - flower.pos.map(|e| e as f32) - Vec3::unit_z() * flower.stalk; - let rpos = flower.rotation * (rpos - 0.5); + let rpos = rpos - Vec3::unit_z() * flower.stalk; let dist_sq = rpos.xy().magnitude_squared(); - let petal_radius_sq = petal_radius.powi(2); + let petal_radius_sq = flower.petal_radius.powi(2); if dist_sq < petal_radius_sq { let petal_height_at = - (dist_sq / petal_radius_sq).powf(1.5) * flower.petal_height; + (dist_sq / petal_radius_sq).powf(1.0) * flower.petal_height; if rpos.z > petal_height_at - 1.0 && rpos.z <= petal_height_at + petal_thickness { let dist_ratio = dist_sq / petal_radius_sq; let yellow = (60.0 * dist_ratio) as u8; - if dist_ratio < 0.175 && rpos.z > petal_height_at { - let near = (rpos.x.atan2(rpos.y)).rem_euclid( - std::f32::consts::TAU / (flower.petals as f32 * 1.5), - ); - let inset = close(near, 0.0, 0.475); - let inset2 = close(near, 0.0, 0.3); - if dist_ratio > inset { + let near = rpos + .x + .atan2(rpos.y) + .rem_euclid(std::f32::consts::TAU / flower.petals as f32); + if dist_ratio < 0.175 { + let red = close_fast(near, 0.0, 0.5, 1); + let purple = close_fast(near, 0.0, 0.35, 1); + if dist_ratio > red || rpos.z < petal_height_at { return Some(Block::new( - BlockKind::Wood, + BlockKind::ArtLeaves, Rgb::new(240, 80 - yellow, 80 - yellow), )); - } else if dist_ratio > inset2 { + } else if dist_ratio > purple { return Some(Block::new( - BlockKind::Wood, + BlockKind::ArtLeaves, Rgb::new(200, 14, 132), )); } else { return Some(Block::new( - BlockKind::Wood, + BlockKind::ArtLeaves, Rgb::new(249, 156, 218), )); } - } else if dist_ratio > 0.3 { - let near = (rpos.x.atan2(rpos.y)) - .rem_euclid(std::f32::consts::TAU / flower.petals as f32); - let inset = close(near, -1.0, 0.9).max(close(near, 1.0, 0.9)); + } else { + let inset = close_fast(near, -1.0, 1.0, 2) + .max(close_fast(near, 1.0, 1.0, 2)); if dist_ratio < inset { return Some(Block::new( - BlockKind::Wood, + BlockKind::ArtLeaves, Rgb::new(240, 80 - yellow, 80 - yellow), )); } - } else { - return Some(Block::new(BlockKind::Wood, Rgb::new(240, 50, 50))); } } @@ -1052,6 +1136,7 @@ fn write_column( let rpos = wposf_warped - pos.map(|e| e as f32); let dist_sq = rpos.xy().magnitude_squared(); if dist_sq < radius.powi(2) { + // Moss if col.marble_mid > (std::f32::consts::PI * rpos.z / *height) .sin() @@ -1069,36 +1154,8 @@ fn write_column( None }; - let ceiling_mold = |wpos: Vec3| { - let wpos = wpos + wpos.xy().yx().with_z(0.0) * 0.2; - let dims = Vec2::new(4.0, 32.0); - let mold_posf = wpos + Vec2::unit_y() * (wpos.x / dims.x).floor() * 89.0 / dims; - let mold_pos = mold_posf.map(|e| e.floor() as i32); - let mut rng = RandomPerm::new(((mold_pos.x << 16) | mold_pos.y) as u32); - - if !void_above && biome.mushroom > 0.9 && ceiling_cover > 0.0 && rng.gen_bool(0.05) { - let mold_length = ((mold_posf.y.fract() - 0.5).abs() * 2.0 * dims.y) - .mul(0.05) - .powf(2.0) - .min(16.0); - let mold_z = z_range.end as f32 - mold_length; - - if Vec2::new(mold_posf.x.fract() * 2.0 - 1.0, (mold_z - wpos.z) / 4.0) - .magnitude_squared() - < 1.0f32 - { - Some(Block::new(BlockKind::GlowingRock, Rgb::new(10, 70, 148))) - } else { - None - } - } else { - None - } - }; - for z in bedrock..z_range.end { let wpos = wpos2d.with_z(z); - let wposf = wpos.map(|e| e as f32); let mut try_spawn_entity = false; canvas.map_resource(wpos, |_block| { if z < z_range.start - 4 && !void_below { @@ -1155,15 +1212,26 @@ fn write_column( biome.icy, ); Block::new( - if rand.chance(wpos, (biome.mushroom * 0.01).max(biome.icy)) { + if rand.chance(wpos, (biome.mushroom * 0.01).max(biome.icy * 0.1)) { BlockKind::GlowingWeakRock } else if rand.chance(wpos, biome.sandy) { BlockKind::Sand + } else if rand.chance(wpos, biome.leafy) { + BlockKind::ArtLeaves } else { BlockKind::WeakRock }, stalactite.map(|e| e as u8), ) + } else if z < ceiling && z >= ceiling_drip { + let color = if biome.mushroom > 0.9 { + Rgb::new(10, 70, 148) + } else if biome.icy > 0.9 { + Rgb::new(120, 140, 255) + } else { + Rgb::new(80, 100, 150) + }; + Block::new(BlockKind::GlowingRock, color) } else if z >= base && z < floor && !void_below && !sky_above { let (net_col, total) = [ ( @@ -1266,8 +1334,11 @@ fn write_column( } else if let Some(sprite) = (z == floor && !void_below && !sky_above) .then(|| { if col.marble_mid > 0.55 - && biome.mushroom > 0.5 - && rand.chance(wpos2d.with_z(1), biome.mushroom * 0.25 * col.marble_mid) + && biome.mushroom > 0.6 + && rand.chance( + wpos2d.with_z(1), + biome.mushroom.powi(2) * 0.2 * col.marble_mid, + ) { [ (SpriteKind::GlowMushroom, 0.5), @@ -1283,7 +1354,10 @@ fn write_column( .map(|s| s.0) } else if col.marble_mid > 0.6 && biome.leafy > 0.4 - && rand.chance(wpos2d.with_z(15), biome.leafy * 0.3 * col.marble_mid) + && rand.chance( + wpos2d.with_z(15), + biome.leafy.powi(2) * 0.25 * col.marble_mid, + ) { let mixed = col.marble.add(col.marble_small.sub(0.5).mul(0.25)); if (0.25..0.45).contains(&mixed) || (0.55..0.75).contains(&mixed) { @@ -1371,9 +1445,11 @@ fn write_column( .choose_weighted(rng, |(_, w)| *w) .ok() .map(|s| s.0) - } else if rand.chance(wpos2d.with_z(23), biome.icy * 0.005) { + } else if biome.icy > 0.5 && rand.chance(wpos2d.with_z(23), biome.icy * 0.005) { Some(SpriteKind::IceCrystal) - } else if rand.chance(wpos2d.with_z(31), biome.icy * biome.mineral * 0.005) { + } else if biome.icy > 0.5 + && rand.chance(wpos2d.with_z(31), biome.icy * biome.mineral * 0.005) + { Some(SpriteKind::GlowIceCrystal) } else if rand.chance(wpos2d.with_z(5), 0.0025) { [ @@ -1437,17 +1513,20 @@ fn write_column( Block::air(sprite) } else if let Some(sprite) = (z == ceiling - 1 && !void_above) .then(|| { - if rand.chance(wpos2d.with_z(3), biome.mushroom * 0.01) { + if biome.mushroom > 0.5 && rand.chance(wpos2d.with_z(3), biome.mushroom * 0.01) + { [(SpriteKind::MycelBlue, 0.75), (SpriteKind::Mold, 1.0)] .choose_weighted(rng, |(_, w)| *w) .ok() .map(|s| s.0) - } else if rand.chance(wpos2d.with_z(4), biome.leafy * 0.015) { + } else if biome.leafy > 0.4 + && rand.chance(wpos2d.with_z(4), biome.leafy * 0.015) + { [ - (SpriteKind::Liana, 1.0), - (SpriteKind::CeilingLanternPlant, 1.5), - (SpriteKind::CeilingLanternFlower, 1.25), - (SpriteKind::CeilingJungleLeafyPlant, 1.0), + (SpriteKind::Liana, 1.5), + (SpriteKind::CeilingLanternPlant, 1.25), + (SpriteKind::CeilingLanternFlower, 1.0), + (SpriteKind::CeilingJungleLeafyPlant, 1.5), ] .choose_weighted(rng, |(_, w)| *w) .ok() @@ -1463,10 +1542,6 @@ fn write_column( .flatten() { Block::air(sprite) - } else if let Some(mold) = - ceiling_mold(wposf).or_else(|| ceiling_mold(wposf.xy().yx().with_z(wposf.z))) - { - mold } else if let Some(structure_block) = get_structure(wpos, rng) { structure_block } else { @@ -1519,11 +1594,13 @@ fn apply_entity_spawns(canvas: &mut Canvas, wpos: Vec3, biome: &Bio ), ( Some("common.entity.wild.aggressive.batfox"), - (biome.leafy.max(biome.barren) + 0.15) * 0.35, - ), - ( - Some("common.entity.wild.aggressive.rocksnapper"), - (biome.leafy.max(biome.barren) + 0.1) * 0.08, + (biome + .leafy + .max(biome.barren) + .max(biome.sandy) + .max(biome.snowy) + + 0.3) + * 0.35, ), ( Some("common.entity.wild.aggressive.cave_salamander"), @@ -1544,7 +1621,7 @@ fn apply_entity_spawns(canvas: &mut Canvas, wpos: Vec3, biome: &Bio // Dusty biome ( Some("common.entity.wild.aggressive.dodarock"), - (biome.dusty.max(biome.barren) + 0.05) * 0.05, + (biome.dusty.max(biome.barren).max(biome.snowy) + 0.05) * 0.05, ), ( Some("common.entity.wild.aggressive.cave_spider"), @@ -1554,17 +1631,13 @@ fn apply_entity_spawns(canvas: &mut Canvas, wpos: Vec3, biome: &Bio Some("common.entity.wild.aggressive.cave_troll"), (biome.dusty + 0.1) * 0.05, ), - ( - Some("common.entity.wild.aggressive.antlion"), - (biome.dusty.min(biome.depth) + 0.0) * 0.01, - ), ( Some("common.entity.wild.peaceful.rat"), (biome.dusty.max(biome.barren) + 0.15) * 0.3, ), ( - Some("common.entity.wild.peaceful.bat"), - (biome.dusty.max(biome.barren) + 0.1) * 0.25, + Some("common.entity.wild.aggressive.bat"), + (biome.dusty.max(biome.sandy).max(biome.snowy) + 0.1) * 0.25, ), // Icy biome ( @@ -1582,7 +1655,7 @@ fn apply_entity_spawns(canvas: &mut Canvas, wpos: Vec3, biome: &Bio // Lava biome ( Some("common.entity.wild.aggressive.lavadrake"), - (biome.fire + 0.0) * 0.15, + (biome.fire + 0.0) * 0.5, ), ( Some("common.entity.wild.peaceful.crawler_molten"), @@ -1592,6 +1665,29 @@ fn apply_entity_spawns(canvas: &mut Canvas, wpos: Vec3, biome: &Bio Some("common.entity.wild.aggressive.red_oni"), (biome.fire + 0.0) * 0.05, ), + // Crystal biome + ( + Some("common.entity.wild.aggressive.basilisk"), + (biome.crystal + 0.1) * 0.1, + ), + // Sandy biome + ( + Some("common.entity.wild.aggressive.antlion"), + (biome.sandy.max(biome.dusty) + 0.1) * 0.025, + ), + ( + Some("common.entity.wild.aggressive.sandshark"), + (biome.sandy + 0.1) * 0.025, + ), + // Snowy biome + ( + Some("common.entity.wild.aggressive.akhlut"), + (biome.snowy.max(biome.icy) + 0.1) * 0.05, + ), + ( + Some("common.entity.wild.aggressive.rocksnapper"), + ((biome.barren).max(biome.snowy) + 0.1) * 0.1, + ), // With depth ( Some("common.entity.wild.aggressive.black_widow"), diff --git a/world/src/lib.rs b/world/src/lib.rs index 15836e8795..cebc3b1e2c 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -465,9 +465,9 @@ impl World { layer::apply_caverns_to(&mut canvas, &mut dynamic_rng); } if index.features.caves { - layer::apply_caves_to(&mut canvas, &mut dynamic_rng); + layer::apply_caves2_to(&mut canvas, &mut dynamic_rng); + // layer::apply_caves_to(&mut canvas, &mut dynamic_rng); } - layer::apply_caves2_to(&mut canvas, &mut dynamic_rng); if index.features.rocks { layer::apply_rocks_to(&mut canvas, &mut dynamic_rng); }