Various tweaks. New ArtLeaves BlockKind

This commit is contained in:
Syniis 2024-02-21 22:39:32 +01:00
parent 71a882a6b7
commit 17a88acd1c
6 changed files with 346 additions and 238 deletions

View File

@ -3,17 +3,17 @@
( (
caverns: false, // TODO: Disabled by default until cave overhaul caverns: false, // TODO: Disabled by default until cave overhaul
caves: false, caves: true,
rocks: false, rocks: true,
shrubs: false, shrubs: true,
trees: false, trees: true,
scatter: true, scatter: true,
paths: false, paths: true,
spots: false, spots: true,
site2_towns: false, site2_towns: true,
site2_giant_trees: true, site2_giant_trees: true,
wildlife_density: 0.0, wildlife_density: 1.0,
peak_naming: false, peak_naming: true,
biome_naming: false, biome_naming: true,
train_tracks: false, // TODO: train stations, train entities train_tracks: false, // TODO: train stations, train entities
) )

View File

@ -671,6 +671,11 @@ impl<const AVERAGE_PALETTE: bool> VoxelImageDecoding for TriPngEncoding<AVERAGE_
g: 206, g: 206,
b: 64, b: 64,
}, },
ArtLeaves => Rgb {
r: 93,
g: 206,
b: 64,
},
GlowingMushroom => Rgb { GlowingMushroom => Rgb {
r: 50, r: 50,
g: 250, g: 250,

View File

@ -55,6 +55,7 @@ make_case_elim!(
Leaves = 0x41, Leaves = 0x41,
GlowingMushroom = 0x42, GlowingMushroom = 0x42,
Ice = 0x43, Ice = 0x43,
ArtLeaves = 0x44,
// 0x43 <= x < 0x50 is reserved for future tree parts // 0x43 <= x < 0x50 is reserved for future tree parts
// Covers all other cases (we sometimes have bizarrely coloured misc blocks, and also we // 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 // often want to experiment with new kinds of block without allocating them a
@ -451,6 +452,7 @@ impl Block {
match self.kind() { match self.kind() {
BlockKind::Water => (0, 0.4), BlockKind::Water => (0, 0.4),
BlockKind::Leaves => (9, 255.0), BlockKind::Leaves => (9, 255.0),
BlockKind::ArtLeaves => (9, 255.0),
BlockKind::Wood => (6, 2.0), BlockKind::Wood => (6, 2.0),
BlockKind::Snow => (6, 2.0), BlockKind::Snow => (6, 2.0),
BlockKind::ArtSnow => (6, 2.0), BlockKind::ArtSnow => (6, 2.0),
@ -488,6 +490,7 @@ impl Block {
// so all is good for empty fluids. // so all is good for empty fluids.
match self.kind() { match self.kind() {
BlockKind::Leaves => Some(0.25), BlockKind::Leaves => Some(0.25),
BlockKind::ArtLeaves => Some(0.25),
BlockKind::Grass => Some(0.5), BlockKind::Grass => Some(0.5),
BlockKind::WeakRock => Some(0.75), BlockKind::WeakRock => Some(0.75),
BlockKind::Snow => Some(0.1), BlockKind::Snow => Some(0.1),

View File

@ -137,7 +137,11 @@ impl VoxelMinimap {
// since otherwise trees would cause ceiling removal to trigger // since otherwise trees would cause ceiling removal to trigger
// when running under a branch. // when running under a branch.
let is_filled = block.map_or(true, |b| { 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)); let rgba = rgba.unwrap_or_else(|| Rgba::new(0, 0, 0, 255));
(rgba, is_filled) (rgba, is_filled)

View File

@ -26,6 +26,10 @@ use std::{
}; };
use vek::*; 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; const CELL_SIZE: i32 = 1536;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -43,6 +47,7 @@ fn to_wpos(cell: Vec2<i32>, level: u32) -> Vec2<i32> {
const AVG_LEVEL_DEPTH: i32 = 120; const AVG_LEVEL_DEPTH: i32 = 120;
const LAYERS: u32 = 4; const LAYERS: u32 = 4;
const MIN_RADIUS: f32 = 8.0;
const MAX_RADIUS: f32 = 64.0; const MAX_RADIUS: f32 = 64.0;
fn node_at(cell: Vec2<i32>, level: u32, land: &Land) -> Option<Node> { fn node_at(cell: Vec2<i32>, level: u32, land: &Land) -> Option<Node> {
@ -89,8 +94,6 @@ pub struct Tunnel {
} }
impl Tunnel { impl Tunnel {
const RADIUS_RANGE: Range<f64> = 8.0..MAX_RADIUS as f64;
fn ctrl_offset(&self) -> Vec2<f32> { fn ctrl_offset(&self) -> Vec2<f32> {
let start = self.a.wpos.map(|e| e as f64 + 0.5); let start = self.a.wpos.map(|e| e as f64 + 0.5);
let end = self.b.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), Vec2::new(start, end),
) { ) {
let dist2 = closest.distance_squared(wposf); 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())) Some((t, closest, dist2.sqrt()))
} else { } else {
None None
@ -128,8 +131,8 @@ impl Tunnel {
let _end = self.b.wpos.map(|e| e as f64 + 0.5); let _end = self.b.wpos.map(|e| e as f64 + 0.5);
if let Some((t, closest, dist)) = self.possibly_near(wposf, 1.0) { if let Some((t, closest, dist)) = self.possibly_near(wposf, 1.0) {
let horizontal = Lerp::lerp_unclamped( let horizontal = Lerp::lerp_unclamped(
Self::RADIUS_RANGE.start, MIN_RADIUS as f64,
Self::RADIUS_RANGE.end, MAX_RADIUS as f64,
(info.index().noise.cave_fbm_nz.get( (info.index().noise.cave_fbm_nz.get(
(closest.with_z(info.land().get_alt_approx(self.a.wpos) as f64) / 256.0) (closest.with_z(info.land().get_alt_approx(self.a.wpos) as f64) / 256.0)
.into_array(), .into_array(),
@ -138,8 +141,8 @@ impl Tunnel {
.powf(3.0), .powf(3.0),
); );
let vertical = Lerp::lerp_unclamped( let vertical = Lerp::lerp_unclamped(
Self::RADIUS_RANGE.start, MIN_RADIUS as f64,
Self::RADIUS_RANGE.end, MAX_RADIUS as f64,
(info.index().noise.cave_fbm_nz.get( (info.index().noise.cave_fbm_nz.get(
(closest.with_z(info.land().get_alt_approx(self.b.wpos) as f64) / 256.0) (closest.with_z(info.land().get_alt_approx(self.b.wpos) as f64) / 256.0)
.into_array(), .into_array(),
@ -536,7 +539,7 @@ struct Flower {
petals: usize, petals: usize,
petal_height: f32, petal_height: f32,
petal_radius: f32, petal_radius: f32,
rotation: Mat3<f32>, // rotation: Mat3<f32>,
} }
#[inline_tweak::tweak_fn] #[inline_tweak::tweak_fn]
@ -569,23 +572,24 @@ fn write_column<R: Rng>(
.sub(0.5) .sub(0.5)
.max(0.0) .max(0.0)
.mul(2.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 // No stalactites near entrances
.mul(((col.alt - z_range.end as f32) / 32.0).clamped(0.0, 1.0)) .mul(((col.alt - z_range.end as f32) / 32.0).clamped(0.0, 1.0))
// Prevent stalactites from blocking cave // Prevent stalactites from blocking cave
.mul(((cave_width + max_height) / 40.0).clamped(0.0, 1.0)) .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) let ceiling_cover = if biome.leafy > 0.3
.max(biome.mushroom - 0.5) || biome.mushroom > 0.5
.max(biome.icy - 0.7) || biome.icy > 0.7
.max(biome.sandy - 0.5) || biome.sandy > 0.5
.max(biome.fire - 0.5) || biome.fire > 0.5
> 0.0
{ {
// 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)) 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) .max(1.0)
.sub( .sub(
if col.marble_mid if col.marble_mid
@ -596,7 +600,7 @@ fn write_column<R: Rng>(
.max(biome.leafy - 0.4) .max(biome.leafy - 0.4)
.max(biome.mushroom - 0.4) .max(biome.mushroom - 0.4)
{ {
cavern_height * col.marble_mid max_height * col.marble_mid
} else { } else {
0.0 0.0
}, },
@ -612,6 +616,7 @@ fn write_column<R: Rng>(
.mul(1.25) .mul(1.25)
.sub(0.5) .sub(0.5)
.max(0.0) .max(0.0)
.mul(((cave_width + max_height) / 32.0).clamped(0.0, 1.0))
.mul(6.0 + cavern_height * 0.5) .mul(6.0 + cavern_height * 0.5)
.mul(biome.fire) .mul(biome.fire)
} else { } else {
@ -633,8 +638,8 @@ fn write_column<R: Rng>(
0.0 0.0
}; };
let height_factor = (max_height / 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 / 32.0).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) let ridge = FastNoise2d::new(38)
.get(wpos2d.map(|e| e as f64 / 512.0)) .get(wpos2d.map(|e| e as f64 / 512.0))
.sub(0.25) .sub(0.25)
@ -652,32 +657,95 @@ fn write_column<R: Rng>(
.get(wpos2d.map(|e| e as f64 / 4.0)) .get(wpos2d.map(|e| e as f64 / 4.0))
.mul(1.15) .mul(1.15)
.add(1.0) .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(((col.alt - z_range.end as f32) / 16.0).clamped(0.0, 1.0))
.mul({ .mul({
let (val, total) = [ let (val, total) = [
(biome.sandy - 0.3, 0.9), (biome.sandy, 0.9),
(biome.dusty - 0.2, 0.5), (biome.dusty, 0.5),
(biome.leafy - 0.5, 0.6), (biome.leafy, 0.6),
(biome.barren - 0.1, 0.6), (biome.barren, 0.6),
] ]
.into_iter() .into_iter()
.fold((0.0, 0.0), |a, x| (a.0 + x.0.max(0.0) * x.1, a.1 + x.1)); .fold((0.0, 0.0), |a, x| (a.0 + x.0.max(0.0) * x.1, a.1 + x.1));
val / total val / total
}) })
.mul(cavern_height * 0.4); .mul(cavern_height * 0.2)
.clamped(0.0, 4.0);
let rand = RandomField::new(37 + level); let rand = RandomField::new(37 + level);
let is_ice = biome.icy + col.marble * 0.2 > 0.5 && col.marble > 0.6; 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 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 bedrock = z_range.start + lava as i32;
let ridge_bedrock = bedrock + (ridge * 0.7) 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 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<i32>, 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<i32>, dynamic_rng: &mut R| { let mut get_structure = |wpos: Vec3<i32>, dynamic_rng: &mut R| {
for (wpos2d, seed) in StructureGen2d::new(34537, 24, 8).get(wpos.xy()) { for (wpos2d, seed) in StructureGen2d::new(34537, 24, 8).get(wpos.xy()) {
@ -696,120 +764,125 @@ fn write_column<R: Rng>(
} }
if biome.mushroom > 0.7 if biome.mushroom > 0.7
&& rng.gen_bool( && vertical > 16.0
0.5 * close(vertical, MAX_RADIUS, 48.0) as f64 && rng.gen_bool(
* close(biome.mushroom, 1.0, 0.7) as f64, 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 { let purp = rng.gen_range(0..50);
pos, Some(CaveStructure::Mushroom(Mushroom {
stalk: 8.0 pos,
+ rng.gen::<f32>().powf(2.0) stalk: 8.0
* (z_range.end - z_range.start - 8) as f32 + rng.gen::<f32>().powf(2.0)
* 0.75, * (z_range.end - z_range.start - 8) as f32
head_color: Rgb::new( * 0.75,
40 + purp, head_color: Rgb::new(
rng.gen_range(60..120), 40 + purp,
rng.gen_range(80..200) + 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<Crystal> = 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),
), ),
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 mut crystals: Vec<Crystal> = Vec::new();
let blue = (rng.gen_range(45.0..75.0) * biome.icy) as u8; 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 { crystals.push(Crystal {
pos, dir,
crystals, length,
color: Rgb::new( radius,
255 - blue * 2, });
255 - blue - purple,
200 + rng.gen_range(25..55), (0..4).for_each(|_| {
), crystals.push(Crystal {
})) dir: Vec3::new(
} else if biome.leafy > 0.8 rng.gen_range(-1.0..1.0),
&& rng.gen_bool( rng.gen_range(-1.0..1.0),
0.2 * (close(vertical, MAX_RADIUS, MAX_RADIUS - 16.0) (dir.z + rng.gen_range(-0.2..0.2)).clamped(0.0, 1.0),
* close(horizontal, MAX_RADIUS, MAX_RADIUS - 12.0) ),
* close(biome.leafy, 1.0, 0.2)) length: length * rng.gen_range(0.3..0.8),
as f64, radius: (radius * rng.gen_range(0.5..0.8)).max(1.0),
) });
{ });
Some(CaveStructure::Flower(Flower {
pos, let purple = rng.gen_range(25..75);
stalk: 4.0 let blue = (rng.gen_range(45.0..75.0) * biome.icy) as u8;
+ rng.gen::<f32>().powf(2.0)
* (z_range.end - z_range.start - 8) as f32 Some(CaveStructure::Crystal(CrystalCluster {
* 0.75, pos,
petals: rng.gen_range(1..5) * 2 + 1, crystals,
petal_height: rng.gen_range(4.0..16.0), color: Rgb::new(
petal_radius: rng.gen_range(8.0..16.0), 255 - blue * 2,
rotation: (Mat3::rotation_x( 255 - blue - purple,
-(rng.gen_bool(1.0) as u32 as f32) * std::f32::consts::PI 200 + rng.gen_range(25..55),
/ rng.gen_range(3.0..16.0), ),
) * Mat3::rotation_y( }))
std::f32::consts::PI / rng.gen_range(3.0..16.0), } else if biome.leafy > 0.8
)) && vertical > 16.0
.transposed(), && horizontal > 8.0
})) && rng.gen_bool(
} else if (biome.leafy > 0.8 || giant_tree_factor > 0.0) 0.25 * (close_fast(vertical, MAX_RADIUS, MAX_RADIUS - 16.0, 2)
&& rng.gen_bool( * close_fast(horizontal, MAX_RADIUS, MAX_RADIUS - 8.0, 2)
(0.4 * close(biome.leafy, 1.0, 0.4).max(1.0 + giant_tree_factor) * biome.leafy) as f64,
as f64) )
.clamped(0.0, 1.0), {
) let petal_radius = rng.gen_range(8.0..16.0);
{ Some(CaveStructure::Flower(Flower {
Some(CaveStructure::GiantRoot { pos,
pos, stalk: 6.0
radius: rng.gen_range( + rng.gen::<f32>().powf(2.0)
2.0..(6.0 * (z_range.end - z_range.start - 8) as f32
+ close(vertical, MAX_RADIUS, MAX_RADIUS / 2.0) * 2.0 * 0.75,
+ close(horizontal, MAX_RADIUS, MAX_RADIUS / 2.0) * 2.0), petals: rng.gen_range(1..5) * 2 + 1,
), petal_height: 0.4
height: (z_range.end - z_range.start) as f32, * petal_radius
}) * (1.0 + rng.gen::<f32>().powf(2.0)),
} else { petal_radius,
None }))
} } 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 structure
} else { } else {
@ -868,8 +941,8 @@ fn write_column<R: Rng>(
block_kind, block_kind,
Rgb::new( Rgb::new(
30, 30,
120 + (radial * 30.0) as u8, 120 + (radial * 40.0) as u8,
180 - (radial * 30.0) as u8, 180 - (radial * 40.0) as u8,
), ),
)); ));
} else if head_dist < 1.0 { } else if head_dist < 1.0 {
@ -949,14 +1022,28 @@ fn write_column<R: Rng>(
} }
}, },
CaveStructure::Flower(flower) => { CaveStructure::Flower(flower) => {
let wposf = wpos.map(|e| e as f32); let wposf = wpos.map(|e| e as f64);
let warp_freq = 1.0 / 16.0;
let rpos = wposf - flower.pos.map(|e| e as f32); 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 stalk_radius = 2.5f32;
let petal_radius = flower.petal_radius;
let petal_thickness = 2.5; let petal_thickness = 2.5;
let dist_sq = rpos.xy().magnitude_squared(); let dist_sq = rpos.xy().magnitude_squared();
if rpos.z < flower.stalk if rpos.z < flower.stalk
&& dist_sq && dist_sq
@ -967,52 +1054,49 @@ fn write_column<R: Rng>(
return Some(Block::new(BlockKind::Wood, Rgb::new(0, 108, 0))); 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 = rpos - Vec3::unit_z() * flower.stalk;
let rpos = flower.rotation * (rpos - 0.5);
let dist_sq = rpos.xy().magnitude_squared(); 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 { if dist_sq < petal_radius_sq {
let petal_height_at = 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 if rpos.z > petal_height_at - 1.0
&& rpos.z <= petal_height_at + petal_thickness && rpos.z <= petal_height_at + petal_thickness
{ {
let dist_ratio = dist_sq / petal_radius_sq; let dist_ratio = dist_sq / petal_radius_sq;
let yellow = (60.0 * dist_ratio) as u8; let yellow = (60.0 * dist_ratio) as u8;
if dist_ratio < 0.175 && rpos.z > petal_height_at { let near = rpos
let near = (rpos.x.atan2(rpos.y)).rem_euclid( .x
std::f32::consts::TAU / (flower.petals as f32 * 1.5), .atan2(rpos.y)
); .rem_euclid(std::f32::consts::TAU / flower.petals as f32);
let inset = close(near, 0.0, 0.475); if dist_ratio < 0.175 {
let inset2 = close(near, 0.0, 0.3); let red = close_fast(near, 0.0, 0.5, 1);
if dist_ratio > inset { let purple = close_fast(near, 0.0, 0.35, 1);
if dist_ratio > red || rpos.z < petal_height_at {
return Some(Block::new( return Some(Block::new(
BlockKind::Wood, BlockKind::ArtLeaves,
Rgb::new(240, 80 - yellow, 80 - yellow), Rgb::new(240, 80 - yellow, 80 - yellow),
)); ));
} else if dist_ratio > inset2 { } else if dist_ratio > purple {
return Some(Block::new( return Some(Block::new(
BlockKind::Wood, BlockKind::ArtLeaves,
Rgb::new(200, 14, 132), Rgb::new(200, 14, 132),
)); ));
} else { } else {
return Some(Block::new( return Some(Block::new(
BlockKind::Wood, BlockKind::ArtLeaves,
Rgb::new(249, 156, 218), Rgb::new(249, 156, 218),
)); ));
} }
} else if dist_ratio > 0.3 { } else {
let near = (rpos.x.atan2(rpos.y)) let inset = close_fast(near, -1.0, 1.0, 2)
.rem_euclid(std::f32::consts::TAU / flower.petals as f32); .max(close_fast(near, 1.0, 1.0, 2));
let inset = close(near, -1.0, 0.9).max(close(near, 1.0, 0.9));
if dist_ratio < inset { if dist_ratio < inset {
return Some(Block::new( return Some(Block::new(
BlockKind::Wood, BlockKind::ArtLeaves,
Rgb::new(240, 80 - yellow, 80 - yellow), 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<R: Rng>(
let rpos = wposf_warped - pos.map(|e| e as f32); let rpos = wposf_warped - pos.map(|e| e as f32);
let dist_sq = rpos.xy().magnitude_squared(); let dist_sq = rpos.xy().magnitude_squared();
if dist_sq < radius.powi(2) { if dist_sq < radius.powi(2) {
// Moss
if col.marble_mid if col.marble_mid
> (std::f32::consts::PI * rpos.z / *height) > (std::f32::consts::PI * rpos.z / *height)
.sin() .sin()
@ -1069,36 +1154,8 @@ fn write_column<R: Rng>(
None None
}; };
let ceiling_mold = |wpos: Vec3<f32>| {
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 { for z in bedrock..z_range.end {
let wpos = wpos2d.with_z(z); let wpos = wpos2d.with_z(z);
let wposf = wpos.map(|e| e as f32);
let mut try_spawn_entity = false; let mut try_spawn_entity = false;
canvas.map_resource(wpos, |_block| { canvas.map_resource(wpos, |_block| {
if z < z_range.start - 4 && !void_below { if z < z_range.start - 4 && !void_below {
@ -1155,15 +1212,26 @@ fn write_column<R: Rng>(
biome.icy, biome.icy,
); );
Block::new( 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 BlockKind::GlowingWeakRock
} else if rand.chance(wpos, biome.sandy) { } else if rand.chance(wpos, biome.sandy) {
BlockKind::Sand BlockKind::Sand
} else if rand.chance(wpos, biome.leafy) {
BlockKind::ArtLeaves
} else { } else {
BlockKind::WeakRock BlockKind::WeakRock
}, },
stalactite.map(|e| e as u8), 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 { } else if z >= base && z < floor && !void_below && !sky_above {
let (net_col, total) = [ let (net_col, total) = [
( (
@ -1266,8 +1334,11 @@ fn write_column<R: Rng>(
} else if let Some(sprite) = (z == floor && !void_below && !sky_above) } else if let Some(sprite) = (z == floor && !void_below && !sky_above)
.then(|| { .then(|| {
if col.marble_mid > 0.55 if col.marble_mid > 0.55
&& biome.mushroom > 0.5 && biome.mushroom > 0.6
&& rand.chance(wpos2d.with_z(1), biome.mushroom * 0.25 * col.marble_mid) && rand.chance(
wpos2d.with_z(1),
biome.mushroom.powi(2) * 0.2 * col.marble_mid,
)
{ {
[ [
(SpriteKind::GlowMushroom, 0.5), (SpriteKind::GlowMushroom, 0.5),
@ -1283,7 +1354,10 @@ fn write_column<R: Rng>(
.map(|s| s.0) .map(|s| s.0)
} else if col.marble_mid > 0.6 } else if col.marble_mid > 0.6
&& biome.leafy > 0.4 && 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)); 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) { if (0.25..0.45).contains(&mixed) || (0.55..0.75).contains(&mixed) {
@ -1371,9 +1445,11 @@ fn write_column<R: Rng>(
.choose_weighted(rng, |(_, w)| *w) .choose_weighted(rng, |(_, w)| *w)
.ok() .ok()
.map(|s| s.0) .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) 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) Some(SpriteKind::GlowIceCrystal)
} else if rand.chance(wpos2d.with_z(5), 0.0025) { } else if rand.chance(wpos2d.with_z(5), 0.0025) {
[ [
@ -1437,17 +1513,20 @@ fn write_column<R: Rng>(
Block::air(sprite) Block::air(sprite)
} else if let Some(sprite) = (z == ceiling - 1 && !void_above) } else if let Some(sprite) = (z == ceiling - 1 && !void_above)
.then(|| { .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)] [(SpriteKind::MycelBlue, 0.75), (SpriteKind::Mold, 1.0)]
.choose_weighted(rng, |(_, w)| *w) .choose_weighted(rng, |(_, w)| *w)
.ok() .ok()
.map(|s| s.0) .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::Liana, 1.5),
(SpriteKind::CeilingLanternPlant, 1.5), (SpriteKind::CeilingLanternPlant, 1.25),
(SpriteKind::CeilingLanternFlower, 1.25), (SpriteKind::CeilingLanternFlower, 1.0),
(SpriteKind::CeilingJungleLeafyPlant, 1.0), (SpriteKind::CeilingJungleLeafyPlant, 1.5),
] ]
.choose_weighted(rng, |(_, w)| *w) .choose_weighted(rng, |(_, w)| *w)
.ok() .ok()
@ -1463,10 +1542,6 @@ fn write_column<R: Rng>(
.flatten() .flatten()
{ {
Block::air(sprite) 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) { } else if let Some(structure_block) = get_structure(wpos, rng) {
structure_block structure_block
} else { } else {
@ -1519,11 +1594,13 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
), ),
( (
Some("common.entity.wild.aggressive.batfox"), Some("common.entity.wild.aggressive.batfox"),
(biome.leafy.max(biome.barren) + 0.15) * 0.35, (biome
), .leafy
( .max(biome.barren)
Some("common.entity.wild.aggressive.rocksnapper"), .max(biome.sandy)
(biome.leafy.max(biome.barren) + 0.1) * 0.08, .max(biome.snowy)
+ 0.3)
* 0.35,
), ),
( (
Some("common.entity.wild.aggressive.cave_salamander"), Some("common.entity.wild.aggressive.cave_salamander"),
@ -1544,7 +1621,7 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
// Dusty biome // Dusty biome
( (
Some("common.entity.wild.aggressive.dodarock"), 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"), Some("common.entity.wild.aggressive.cave_spider"),
@ -1554,17 +1631,13 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
Some("common.entity.wild.aggressive.cave_troll"), Some("common.entity.wild.aggressive.cave_troll"),
(biome.dusty + 0.1) * 0.05, (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"), Some("common.entity.wild.peaceful.rat"),
(biome.dusty.max(biome.barren) + 0.15) * 0.3, (biome.dusty.max(biome.barren) + 0.15) * 0.3,
), ),
( (
Some("common.entity.wild.peaceful.bat"), Some("common.entity.wild.aggressive.bat"),
(biome.dusty.max(biome.barren) + 0.1) * 0.25, (biome.dusty.max(biome.sandy).max(biome.snowy) + 0.1) * 0.25,
), ),
// Icy biome // Icy biome
( (
@ -1582,7 +1655,7 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
// Lava biome // Lava biome
( (
Some("common.entity.wild.aggressive.lavadrake"), 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"), Some("common.entity.wild.peaceful.crawler_molten"),
@ -1592,6 +1665,29 @@ fn apply_entity_spawns<R: Rng>(canvas: &mut Canvas, wpos: Vec3<i32>, biome: &Bio
Some("common.entity.wild.aggressive.red_oni"), Some("common.entity.wild.aggressive.red_oni"),
(biome.fire + 0.0) * 0.05, (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 // With depth
( (
Some("common.entity.wild.aggressive.black_widow"), Some("common.entity.wild.aggressive.black_widow"),

View File

@ -465,9 +465,9 @@ impl World {
layer::apply_caverns_to(&mut canvas, &mut dynamic_rng); layer::apply_caverns_to(&mut canvas, &mut dynamic_rng);
} }
if index.features.caves { 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 { if index.features.rocks {
layer::apply_rocks_to(&mut canvas, &mut dynamic_rng); layer::apply_rocks_to(&mut canvas, &mut dynamic_rng);
} }