From 2050bce77db5446385e005964688237c1bdf9056 Mon Sep 17 00:00:00 2001 From: Isse Date: Sun, 15 May 2022 10:16:53 +0000 Subject: [PATCH] Water caves --- CHANGELOG.md | 1 + .../cave_scatter/deep_water_ceiling.ron | 3 + .../common/cave_scatter/deep_water_floor.ron | 13 ++ .../cave_scatter/shallow_water_ceiling.ron | 3 + .../cave_scatter/shallow_water_floor.ron | 11 ++ assets/voxygen/shaders/fluid-frag/shiny.glsl | 2 +- world/src/civ/mod.rs | 61 +++++- world/src/layer/mod.rs | 175 +++++++++++++----- world/src/sim/way.rs | 6 + 9 files changed, 222 insertions(+), 53 deletions(-) create mode 100644 assets/common/cave_scatter/deep_water_ceiling.ron create mode 100644 assets/common/cave_scatter/deep_water_floor.ron create mode 100644 assets/common/cave_scatter/shallow_water_ceiling.ron create mode 100644 assets/common/cave_scatter/shallow_water_floor.ron diff --git a/CHANGELOG.md b/CHANGELOG.md index 61586bb5c0..668ec5c325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add FOV check for agents scanning for targets they are hostile to - Implemented an LoD system for objects, making trees visible far beyond the view distance - Add stealth stat on Bag UI +- Water caves ### Changed diff --git a/assets/common/cave_scatter/deep_water_ceiling.ron b/assets/common/cave_scatter/deep_water_ceiling.ron new file mode 100644 index 0000000000..00257710c4 --- /dev/null +++ b/assets/common/cave_scatter/deep_water_ceiling.ron @@ -0,0 +1,3 @@ +[ + (1, CrystalHigh), +] \ No newline at end of file diff --git a/assets/common/cave_scatter/deep_water_floor.ron b/assets/common/cave_scatter/deep_water_floor.ron new file mode 100644 index 0000000000..1e632af078 --- /dev/null +++ b/assets/common/cave_scatter/deep_water_floor.ron @@ -0,0 +1,13 @@ +[ + (40, Velorite), + (40, VeloriteFrag), + (30, AmethystSmall), + (30, TopazSmall), + (16, SapphireSmall), + (100, CrystalLow), + (12, EmeraldSmall), + (15, Cobalt), + (40, Coal), + (70, Iron), + (10, RubySmall), +] \ No newline at end of file diff --git a/assets/common/cave_scatter/shallow_water_ceiling.ron b/assets/common/cave_scatter/shallow_water_ceiling.ron new file mode 100644 index 0000000000..d073162c87 --- /dev/null +++ b/assets/common/cave_scatter/shallow_water_ceiling.ron @@ -0,0 +1,3 @@ +[ + (1, CrystalHigh), +] diff --git a/assets/common/cave_scatter/shallow_water_floor.ron b/assets/common/cave_scatter/shallow_water_floor.ron new file mode 100644 index 0000000000..5ef7c1cabf --- /dev/null +++ b/assets/common/cave_scatter/shallow_water_floor.ron @@ -0,0 +1,11 @@ +[ + (110, Stones), + (5, AmethystSmall), + (5, TopazSmall), + (15, Tin), + (12, Copper), + (15, Iron), + (125, BullKelp), + (125, WavyAlgae), + (5, Seashells), +] diff --git a/assets/voxygen/shaders/fluid-frag/shiny.glsl b/assets/voxygen/shaders/fluid-frag/shiny.glsl index 776cb05889..e07f8cc92d 100644 --- a/assets/voxygen/shaders/fluid-frag/shiny.glsl +++ b/assets/voxygen/shaders/fluid-frag/shiny.glsl @@ -192,7 +192,7 @@ void main() { reflect_color *= f_light; // Prevent the sky affecting light when underground - float not_underground = clamp((f_pos.z - f_alt) / 128.0 + 1.0, 0.0, 1.0); + float not_underground = clamp((f_pos.z - f_alt) / 32.0 + 1.0, 0.0, 1.0); reflect_color *= not_underground; // /*const */vec3 water_color = srgb_to_linear(vec3(0.2, 0.5, 1.0)); // /*const */vec3 water_color = srgb_to_linear(vec3(0.8, 0.9, 1.0)); diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index 056a5cb329..7ee2e485fb 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -95,9 +95,7 @@ impl Civs { let mut ctx = GenCtx { sim, rng }; info!("starting cave generation"); - for _ in 0..ctx.sim.get_size().product() / 10_000 { - this.generate_cave(&mut ctx); - } + this.generate_caves(&mut ctx); info!("starting civilisation creation"); let mut start_locations: Vec> = Vec::new(); @@ -335,8 +333,46 @@ impl Civs { this } + fn generate_caves(&mut self, ctx: &mut GenCtx) { + let mut water_caves = Vec::new(); + for _ in 0..ctx.sim.get_size().product() / 10_000 { + self.generate_cave(ctx, &mut water_caves); + } + + // Floodfills cave water. + while let Some(loc) = water_caves.pop() { + let cave = ctx.sim.get(loc).unwrap().cave.1; + for l in NEIGHBORS { + let l = loc + l; + if let Some(o_cave) = ctx.sim.get_mut(l).map(|c| &mut c.cave.1) { + // Contains cave + if o_cave.alt != 0.0 { + let should_fill = o_cave.water_alt < cave.water_alt + && o_cave.alt - o_cave.width < cave.water_alt as f32; + if should_fill { + o_cave.water_alt = cave.water_alt; + o_cave.water_dist = 0.0; + water_caves.push(l); + } + // If we don't fill and the cave has no water, continue filling distance + else if o_cave.water_alt == i32::MIN + && o_cave.water_dist > cave.water_dist + 1.0 + { + o_cave.water_dist = cave.water_dist + 1.0; + water_caves.push(l); + } + } + } + } + } + } + // TODO: Move this - fn generate_cave(&mut self, ctx: &mut GenCtx) { + fn generate_cave( + &mut self, + ctx: &mut GenCtx, + submerged_cave_chunks: &mut Vec>, + ) { let mut pos = ctx .sim .get_size() @@ -384,7 +420,6 @@ impl Civs { ctx.sim.get_mut(locs[2].0).unwrap().cave.0.neighbors |= 1 << ((to_next_idx as u8 + 4) % 8); } - for loc in path.iter() { let mut chunk = ctx.sim.get_mut(loc.0).unwrap(); let depth = loc.1 * 250.0 - 20.0; @@ -396,7 +431,23 @@ impl Civs { if chunk.cave.1.alt + chunk.cave.1.width + 5.0 > chunk.alt { chunk.spawn_rate = 0.0; } + let cave_min_alt = chunk.cave.1.alt - chunk.cave.1.width; + let cave_max_alt = chunk.cave.1.alt + chunk.cave.1.width; + + let submerged = chunk.alt - 2.0 < chunk.water_alt + && chunk.alt < cave_max_alt + && cave_min_alt < chunk.water_alt + && chunk.river.near_water() + // Only do this for caves at the sea level for now. + // The reason being that floodfilling from a water alt to an alt lower than the water alt causes problems. + && chunk.water_alt <= CONFIG.sea_level; + if submerged { + submerged_cave_chunks.push(loc.0); + chunk.cave.1.water_alt = chunk.water_alt as i32; + chunk.cave.1.water_dist = 0.0; + } } + self.caves.insert(CaveInfo { location: ( path.first().unwrap().0 * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32), diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 66b1528b8a..29030d7a0d 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -136,8 +136,13 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { let cave_floor = 0.0 - 0.5 * (1.0 - cave_x.powi(2)).max(0.0).sqrt() * cave.width; let cave_height = (1.0 - cave_x.powi(2)).max(0.0).sqrt() * cave.width; + let t = cave.water_dist.min(1.0); // Abs units - let cave_base = (cave.alt + cave_floor) as i32; + let cave_base = Lerp::lerp( + cave.alt + cave_floor, + (cave.water_alt as f32).max(cave.alt + cave_floor), + t, + ) as i32; let cave_roof = (cave.alt + cave_height) as i32; for z in cave_base..cave_roof { @@ -148,14 +153,23 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { .into_array(), ) < 0.0 { - // If the block a little above is liquid, we should stop carving out the cave in - // order to leave a ceiling, and not floating water - if canvas.get(Vec3::new(wpos2d.x, wpos2d.y, z + 2)).is_liquid() { + // If the block a little above is liquid, and the water level is lower, we + // should stop carving out the cave in order to leave a + // ceiling, and not floating water. + if z >= cave.water_alt + && canvas.get(Vec3::new(wpos2d.x, wpos2d.y, z + 2)).is_liquid() + { break; } + let empty_block = if z < cave.water_alt { + Block::water(SpriteKind::Empty) + } else { + EMPTY_AIR + }; + canvas.map(Vec3::new(wpos2d.x, wpos2d.y, z), |b| { - if b.is_liquid() { b } else { EMPTY_AIR } + if b.is_liquid() { b } else { empty_block } }); } } @@ -218,7 +232,9 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { //make pits for z in cave_base - pit_depth..cave_base { if pit_condition && (cave_roof - cave_base) > 10 { - let kind = if z < (cave_base - pit_depth) + (3 * pit_depth / 4) { + let kind = if z < cave.water_alt { + BlockKind::Water + } else if z < (cave_base - pit_depth) + (3 * pit_depth / 4) { BlockKind::Lava } else { BlockKind::Air @@ -335,9 +351,34 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { } else { cave_base - floor_dist }; - - // Scatter things in caves - if cave_depth > 40.0 && cave_depth < 80.0 { + // Scatter things on cave floors + if cave_floor_adjusted + 1 < cave.water_alt { + if cave_depth > 40.0 && cave_depth < 80.0 { + if rng.gen::() < 0.14 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { + let kind = *Lottery::::load_expect( + "common.cave_scatter.shallow_water_floor", + ) + .read() + .choose(); + canvas.map( + Vec3::new(wpos2d.x, wpos2d.y, cave_floor_adjusted), + |block| block.with_sprite(kind), + ); + } + } else if rng.gen::() < 0.065 * (cave_x.max(0.5).powf(4.0)) + && !vein_condition + && cave_depth > 40.0 + { + let kind = + *Lottery::::load_expect("common.cave_scatter.deep_water_floor") + .read() + .choose(); + canvas.map( + Vec3::new(wpos2d.x, wpos2d.y, cave_floor_adjusted), + |block| block.with_sprite(kind), + ); + }; + } else if cave_depth > 40.0 && cave_depth < 80.0 { if rng.gen::() < 0.14 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = *Lottery::::load_expect("common.cave_scatter.shallow_floor") @@ -348,16 +389,6 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { |block| block.with_sprite(kind), ); } - if rng.gen::() < 0.3 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition { - let kind = - *Lottery::::load_expect("common.cave_scatter.shallow_ceiling") - .read() - .choose(); - canvas.map( - Vec3::new(wpos2d.x, wpos2d.y, cave_roof_adjusted - 1), - |block| block.with_sprite(kind), - ); - } } else if cave_depth < 200.0 && cave_depth > 80.0 { if rng.gen::() < 0.065 * (cave_x.max(0.5).powf(4.0)) && !vein_condition { let kind = @@ -369,6 +400,59 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { |block| block.with_sprite(kind), ); } + } else if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) + && cave_depth > 40.0 + && !vein_condition + { + let kind = *Lottery::::load_expect("common.cave_scatter.dark_floor") + .read() + .choose(); + canvas.map( + Vec3::new(wpos2d.x, wpos2d.y, cave_floor_adjusted), + |block| block.with_sprite(kind), + ); + }; + + // Scatter things on cave ceilings + if cave_roof_adjusted - 1 < cave.water_alt { + if cave_depth > 40.0 && cave_depth < 80.0 { + if rng.gen::() < 0.02 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition { + let kind = *Lottery::::load_expect( + "common.cave_scatter.shallow_water_ceiling", + ) + .read() + .choose(); + canvas.map( + Vec3::new(wpos2d.x, wpos2d.y, cave_roof_adjusted - 1), + |block| block.with_sprite(kind), + ); + } + } else if rng.gen::() < 0.1 * (cave_x.max(0.5).powf(4.0)) + && !ridge_condition + && cave_depth > 40.0 + { + let kind = *Lottery::::load_expect( + "common.cave_scatter.deep_water_ceiling", + ) + .read() + .choose(); + canvas.map( + Vec3::new(wpos2d.x, wpos2d.y, cave_roof_adjusted - 1), + |block| block.with_sprite(kind), + ); + }; + } else if cave_depth > 40.0 && cave_depth < 80.0 { + if rng.gen::() < 0.3 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition { + let kind = + *Lottery::::load_expect("common.cave_scatter.shallow_ceiling") + .read() + .choose(); + canvas.map( + Vec3::new(wpos2d.x, wpos2d.y, cave_roof_adjusted - 1), + |block| block.with_sprite(kind), + ); + } + } else if cave_depth < 200.0 && cave_depth > 80.0 { if rng.gen::() < 0.3 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition { let kind = *Lottery::::load_expect("common.cave_scatter.deep_ceiling") @@ -379,33 +463,17 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) { |block| block.with_sprite(kind), ); } - } else { - if rng.gen::() < 0.08 * (cave_x.max(0.5).powf(4.0)) - && cave_depth > 40.0 - && !vein_condition - { - let kind = - *Lottery::::load_expect("common.cave_scatter.dark_floor") - .read() - .choose(); - canvas.map( - Vec3::new(wpos2d.x, wpos2d.y, cave_floor_adjusted), - |block| block.with_sprite(kind), - ); - } - if rng.gen::() < 0.02 * (cave_x.max(0.5).powf(4.0)) - && !ridge_condition - && cave_depth > 40.0 - { - let kind = - *Lottery::::load_expect("common.cave_scatter.dark_ceiling") - .read() - .choose(); - canvas.map( - Vec3::new(wpos2d.x, wpos2d.y, cave_roof_adjusted - 1), - |block| block.with_sprite(kind), - ); - } + } else if rng.gen::() < 0.02 * (cave_x.max(0.5).powf(4.0)) + && !ridge_condition + && cave_depth > 40.0 + { + let kind = *Lottery::::load_expect("common.cave_scatter.dark_ceiling") + .read() + .choose(); + canvas.map( + Vec3::new(wpos2d.x, wpos2d.y, cave_roof_adjusted - 1), + |block| block.with_sprite(kind), + ); }; } }); @@ -460,7 +528,20 @@ pub fn apply_caves_supplement<'a>( { let entity = EntityInfo::at(wpos2d.map(|e| e as f32).with_z(z as f32)); let entity = { - let asset = if cave_depth < 70.0 { + let asset = if z < cave.water_alt { + if cave_depth < 190.0 { + match dynamic_rng.gen_range(0..2) { + 0 => "common.entity.wild.aggressive.sea_crocodile", + _ => "common.entity.wild.aggressive.hakulaq", + } + } else { + match dynamic_rng.gen_range(0..3) { + 0 => "common.entity.wild.aggressive.sea_crocodile", + 1 => "common.entity.wild.aggressive.hakulaq", + _ => "common.entity.wild.aggressive.akhlut", + } + } + } else if cave_depth < 70.0 { match dynamic_rng.gen_range(0..4) { 0 => "common.entity.wild.peaceful.truffler", 1 => "common.entity.wild.aggressive.dodarock", diff --git a/world/src/sim/way.rs b/world/src/sim/way.rs index 1f8b4252e3..663c841976 100644 --- a/world/src/sim/way.rs +++ b/world/src/sim/way.rs @@ -47,6 +47,8 @@ impl Path { pub struct Cave { pub width: f32, // Actually radius pub alt: f32, // Actually radius + pub water_alt: i32, + pub water_dist: f32, } impl Default for Cave { @@ -54,6 +56,8 @@ impl Default for Cave { Self { width: 32.0, alt: 0.0, + water_alt: i32::MIN, + water_dist: f32::INFINITY, } } } @@ -65,6 +69,8 @@ impl Lerp for Cave { Self { width: Lerp::lerp(from.width, to.width, factor), alt: Lerp::lerp(from.alt, to.alt, factor), + water_alt: from.water_alt.max(to.water_alt), + water_dist: Lerp::lerp(from.water_dist, to.water_dist, factor), } } }