Water caves

This commit is contained in:
Isse 2022-05-15 10:16:53 +00:00 committed by Joshua Barretto
parent 2855627ca2
commit 2050bce77d
9 changed files with 222 additions and 53 deletions

View File

@ -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

View File

@ -0,0 +1,3 @@
[
(1, CrystalHigh),
]

View File

@ -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),
]

View File

@ -0,0 +1,3 @@
[
(1, CrystalHigh),
]

View File

@ -0,0 +1,11 @@
[
(110, Stones),
(5, AmethystSmall),
(5, TopazSmall),
(15, Tin),
(12, Copper),
(15, Iron),
(125, BullKelp),
(125, WavyAlgae),
(5, Seashells),
]

View File

@ -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));

View File

@ -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<Vec2<i32>> = Vec::new();
@ -335,8 +333,46 @@ impl Civs {
this
}
fn generate_caves(&mut self, ctx: &mut GenCtx<impl Rng>) {
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<impl Rng>) {
fn generate_cave(
&mut self,
ctx: &mut GenCtx<impl Rng>,
submerged_cave_chunks: &mut Vec<Vec2<i32>>,
) {
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),

View File

@ -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,12 +351,13 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
} else {
cave_base - floor_dist
};
// Scatter things in caves
// 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::<f32>() < 0.14 * (cave_x.max(0.5).powf(4.0)) && !vein_condition {
let kind =
*Lottery::<SpriteKind>::load_expect("common.cave_scatter.shallow_floor")
let kind = *Lottery::<SpriteKind>::load_expect(
"common.cave_scatter.shallow_water_floor",
)
.read()
.choose();
canvas.map(
@ -348,13 +365,27 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|block| block.with_sprite(kind),
);
}
if rng.gen::<f32>() < 0.3 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition {
} else if rng.gen::<f32>() < 0.065 * (cave_x.max(0.5).powf(4.0))
&& !vein_condition
&& cave_depth > 40.0
{
let kind =
*Lottery::<SpriteKind>::load_expect("common.cave_scatter.shallow_ceiling")
*Lottery::<SpriteKind>::load_expect("common.cave_scatter.deep_water_floor")
.read()
.choose();
canvas.map(
Vec3::new(wpos2d.x, wpos2d.y, cave_roof_adjusted - 1),
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::<f32>() < 0.14 * (cave_x.max(0.5).powf(4.0)) && !vein_condition {
let kind =
*Lottery::<SpriteKind>::load_expect("common.cave_scatter.shallow_floor")
.read()
.choose();
canvas.map(
Vec3::new(wpos2d.x, wpos2d.y, cave_floor_adjusted),
|block| block.with_sprite(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::<f32>() < 0.08 * (cave_x.max(0.5).powf(4.0))
&& cave_depth > 40.0
&& !vein_condition
{
let kind = *Lottery::<SpriteKind>::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::<f32>() < 0.02 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition {
let kind = *Lottery::<SpriteKind>::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::<f32>() < 0.1 * (cave_x.max(0.5).powf(4.0))
&& !ridge_condition
&& cave_depth > 40.0
{
let kind = *Lottery::<SpriteKind>::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::<f32>() < 0.3 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition {
let kind =
*Lottery::<SpriteKind>::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::<f32>() < 0.3 * (cave_x.max(0.5).powf(4.0)) && !ridge_condition {
let kind =
*Lottery::<SpriteKind>::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::<f32>() < 0.08 * (cave_x.max(0.5).powf(4.0))
&& cave_depth > 40.0
&& !vein_condition
{
let kind =
*Lottery::<SpriteKind>::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::<f32>() < 0.02 * (cave_x.max(0.5).powf(4.0))
} else if rng.gen::<f32>() < 0.02 * (cave_x.max(0.5).powf(4.0))
&& !ridge_condition
&& cave_depth > 40.0
{
let kind =
*Lottery::<SpriteKind>::load_expect("common.cave_scatter.dark_ceiling")
let kind = *Lottery::<SpriteKind>::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",

View File

@ -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),
}
}
}