mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/shrubs' into 'master'
Shrubs (and water fixes) See merge request veloren/veloren!2796
This commit is contained in:
commit
38ecfe7c8b
@ -13,12 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Added a crafting station icon to the crafting menu sidebar for items that could be crafted at a crafting station
|
||||
- Added a setting to disable the hotkey hints
|
||||
- Added a credits screen in the main menu which shows attributions for assets
|
||||
- Shrubs, a system for spawning smaller tree-like plants into the world.
|
||||
- Waterfalls
|
||||
- Sailing boat (currently requires spawning in)
|
||||
|
||||
### Changed
|
||||
|
||||
- Made dungeon tiers 3, 4, and 5 more common
|
||||
- Put date at the begining of the log file instead of the end to allow MIME type recognition
|
||||
- Tweaked CR and exp calculation formula
|
||||
- Sprite spawn rates
|
||||
|
||||
### Removed
|
||||
|
||||
@ -27,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- The menu map now properly handles dragging the map, zooming, and setting the waypoint when hovering icons
|
||||
- Falling through an airship in flight should no longer be possible (although many issues with airship physics remain)
|
||||
- Avoided black hexagons when bloom is enabled by suppressing NaN/Inf pixels during the first bloom blur pass
|
||||
- Many know water generation problems
|
||||
|
||||
## [0.11.0] - 2021-09-11
|
||||
|
||||
|
@ -47,4 +47,26 @@
|
||||
central: ("air_balloon.rudder"),
|
||||
),
|
||||
),
|
||||
SailBoat: (
|
||||
bone0: (
|
||||
offset: (-6.5, -15.5, 0.0),
|
||||
phys_offset: (0.0, 0.0, 0.0),
|
||||
central: ("sail_boat.structure"),
|
||||
),
|
||||
bone1: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
phys_offset: (0.0, 0.0, 0.0),
|
||||
central: ("empty"),
|
||||
),
|
||||
bone2: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
phys_offset: (0.0, 0.0, 0.0),
|
||||
central: ("empty"),
|
||||
),
|
||||
bone3: (
|
||||
offset: (-1.5, -6.0, -5.0),
|
||||
phys_offset: (0.0, 0.0, 0.0),
|
||||
central: ("empty"),
|
||||
),
|
||||
),
|
||||
})
|
||||
|
BIN
assets/server/voxel/sail_boat/structure.vox
(Stored with Git LFS)
Normal file
BIN
assets/server/voxel/sail_boat/structure.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -204,7 +204,7 @@ void main() {
|
||||
const float R_s2s1 = pow((1.0 - 1.3325) / (1.0 + 1.3325), 2);
|
||||
const float R_s1s2 = pow((1.3325 - 1.0) / (1.3325 + 1.0), 2);
|
||||
// float faces_fluid = faces_fluid && f_pos.z <= floor(f_alt);
|
||||
float fluid_alt = max(f_pos.z + 1, floor(f_alt));
|
||||
float fluid_alt = max(f_pos.z + 1, floor(f_alt + 1));
|
||||
float R_s = /*(f_pos.z < f_alt)*/faces_fluid /*&& f_pos.z <= fluid_alt*/ ? mix(R_s2s1 * R_s1s0, R_s1s0, medium.x) : mix(R_s2s0, R_s1s2 * R_s2s0, medium.x);
|
||||
|
||||
// vec3 surf_color = /*srgb_to_linear*/(f_col);
|
||||
|
32
assets/world/manifests/shrubs/jungle.ron
Normal file
32
assets/world/manifests/shrubs/jungle.ron
Normal file
@ -0,0 +1,32 @@
|
||||
#![enable(unwrap_newtypes)]
|
||||
|
||||
[
|
||||
(
|
||||
specifier: "world.shrub.1",
|
||||
center: (6, 6, 2),
|
||||
),
|
||||
(
|
||||
specifier: "world.shrub.jungle.bush-0",
|
||||
center: (5, 5, 3),
|
||||
),
|
||||
(
|
||||
specifier: "world.shrub.jungle.bush-1",
|
||||
center: (5, 5, 2),
|
||||
),
|
||||
(
|
||||
specifier: "world.shrub.jungle.bush-2",
|
||||
center: (5, 5, 3),
|
||||
),
|
||||
(
|
||||
specifier: "world.shrub.jungle.bush-3",
|
||||
center: (5, 5, 3),
|
||||
),
|
||||
(
|
||||
specifier: "world.shrub.jungle.bush-4",
|
||||
center: (5, 5, 4),
|
||||
),
|
||||
(
|
||||
specifier: "world.shrub.jungle.bush-5",
|
||||
center: (5, 5, 5),
|
||||
),
|
||||
]
|
BIN
assets/world/shrub/1.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/shrub/1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/shrub/jungle/bush-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/shrub/jungle/bush-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/shrub/jungle/bush-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/shrub/jungle/bush-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/shrub/jungle/bush-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/shrub/jungle/bush-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/shrub/jungle/bush-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/shrub/jungle/bush-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/shrub/jungle/bush-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/shrub/jungle/bush-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/shrub/jungle/bush-5.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/shrub/jungle/bush-5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -854,6 +854,7 @@ impl Body {
|
||||
Body::Ship(ship) => match ship {
|
||||
ship::Body::DefaultAirship => [0.0, 0.0, 10.0],
|
||||
ship::Body::AirBalloon => [0.0, 0.0, 5.0],
|
||||
ship::Body::SailBoat => [-2.0, -5.0, 4.0],
|
||||
},
|
||||
_ => [0.0, 0.0, 0.0],
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use crate::{
|
||||
comp::{Density, Mass},
|
||||
consts::AIR_DENSITY,
|
||||
consts::{AIR_DENSITY, WATER_DENSITY},
|
||||
make_case_elim,
|
||||
};
|
||||
use rand::prelude::SliceRandom;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vek::Vec3;
|
||||
|
||||
pub const ALL_BODIES: [Body; 2] = [Body::DefaultAirship, Body::AirBalloon];
|
||||
pub const ALL_BODIES: [Body; 3] = [Body::DefaultAirship, Body::AirBalloon, Body::SailBoat];
|
||||
|
||||
make_case_elim!(
|
||||
body,
|
||||
@ -16,6 +16,7 @@ make_case_elim!(
|
||||
pub enum Body {
|
||||
DefaultAirship = 0,
|
||||
AirBalloon = 1,
|
||||
SailBoat = 2,
|
||||
}
|
||||
);
|
||||
|
||||
@ -35,6 +36,7 @@ impl Body {
|
||||
match self {
|
||||
Body::DefaultAirship => "airship_human.structure",
|
||||
Body::AirBalloon => "air_balloon.structure",
|
||||
Body::SailBoat => "sail_boat.structure",
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,15 +44,21 @@ impl Body {
|
||||
match self {
|
||||
Body::DefaultAirship => Vec3::new(25.0, 50.0, 40.0),
|
||||
Body::AirBalloon => Vec3::new(25.0, 50.0, 40.0),
|
||||
Body::SailBoat => Vec3::new(13.0, 31.0, 3.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn balloon_vol(&self) -> f32 {
|
||||
let spheroid_vol = |equat_d: f32, polar_d: f32| -> f32 {
|
||||
(std::f32::consts::PI / 6.0) * equat_d.powi(2) * polar_d
|
||||
};
|
||||
let dim = self.dimensions();
|
||||
spheroid_vol(dim.z, dim.y)
|
||||
match self {
|
||||
Body::DefaultAirship | Body::AirBalloon => {
|
||||
let spheroid_vol = |equat_d: f32, polar_d: f32| -> f32 {
|
||||
(std::f32::consts::PI / 6.0) * equat_d.powi(2) * polar_d
|
||||
};
|
||||
let dim = self.dimensions();
|
||||
spheroid_vol(dim.z, dim.y)
|
||||
},
|
||||
_ => 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn hull_vol(&self) -> f32 {
|
||||
@ -66,14 +74,19 @@ impl Body {
|
||||
Density(ratio * oak_density + (1.0 - ratio) * AIR_DENSITY)
|
||||
}
|
||||
|
||||
pub fn density(&self) -> Density { Density(AIR_DENSITY) }
|
||||
pub fn density(&self) -> Density {
|
||||
match self {
|
||||
Body::DefaultAirship | Body::AirBalloon => Density(AIR_DENSITY),
|
||||
_ => Density(AIR_DENSITY * 0.8 + WATER_DENSITY * 0.2), // Most boats should be buoyant
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mass(&self) -> Mass { Mass((self.hull_vol() + self.balloon_vol()) * self.density().0) }
|
||||
|
||||
pub fn can_fly(&self) -> bool {
|
||||
match self {
|
||||
Body::DefaultAirship | Body::AirBalloon => true,
|
||||
}
|
||||
pub fn can_fly(&self) -> bool { matches!(self, Body::DefaultAirship | Body::AirBalloon) }
|
||||
|
||||
pub fn has_water_thrust(&self) -> bool {
|
||||
!self.can_fly() // TODO: Differentiate this more carefully
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,7 @@ impl Body {
|
||||
quadruped_low::Species::Lavadrake => 1.7,
|
||||
_ => 2.0,
|
||||
},
|
||||
Body::Ship(ship) if ship.has_water_thrust() => 0.1,
|
||||
Body::Ship(_) => 0.035,
|
||||
}
|
||||
}
|
||||
@ -180,7 +181,7 @@ impl Body {
|
||||
/// Returns thrust force if the body type can swim, otherwise None
|
||||
pub fn swim_thrust(&self) -> Option<f32> {
|
||||
match self {
|
||||
Body::Object(_) | Body::Ship(_) => None,
|
||||
Body::Object(_) => None,
|
||||
Body::BipedLarge(_) | Body::Golem(_) => Some(200.0 * self.mass().0),
|
||||
Body::BipedSmall(_) => Some(100.0 * self.mass().0),
|
||||
Body::BirdMedium(_) => Some(50.0 * self.mass().0),
|
||||
@ -200,6 +201,8 @@ impl Body {
|
||||
Body::QuadrupedLow(_) => Some(300.0 * self.mass().0),
|
||||
Body::QuadrupedMedium(_) => Some(300.0 * self.mass().0),
|
||||
Body::QuadrupedSmall(_) => Some(300.0 * self.mass().0),
|
||||
Body::Ship(ship) if ship.has_water_thrust() => Some(1500.0 * self.mass().0),
|
||||
Body::Ship(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -551,8 +551,11 @@ impl<'a> MapConfig<'a> {
|
||||
downhill_wpos,
|
||||
);
|
||||
let (_t, _pt, dist) = if let Some((t, pt, dist)) =
|
||||
quadratic_nearest_point(&coeffs, wposf)
|
||||
{
|
||||
quadratic_nearest_point(
|
||||
&coeffs,
|
||||
wposf,
|
||||
Vec2::new(neighbor_wpos, downhill_wpos),
|
||||
) {
|
||||
(t, pt, dist)
|
||||
} else {
|
||||
let ndist = wposf.distance_squared(neighbor_wpos);
|
||||
|
@ -293,7 +293,53 @@ pub fn river_spline_coeffs(
|
||||
pub fn quadratic_nearest_point(
|
||||
spline: &Vec3<Vec2<f64>>,
|
||||
point: Vec2<f64>,
|
||||
_line: Vec2<Vec2<f64>>, // Used for alternative distance functions below
|
||||
) -> Option<(f64, Vec2<f64>, f64)> {
|
||||
//let eval_at = |t: f64| spline.x * t * t + spline.y * t + spline.z;
|
||||
|
||||
// Linear
|
||||
|
||||
// let line = LineSegment2 {
|
||||
// start: line.x,
|
||||
// end: line.y,
|
||||
// };
|
||||
// let len_sq = line.start.distance_squared(line.end);
|
||||
// let t = ((point - line.start).dot(line.end - line.start) /
|
||||
// len_sq).clamped(0.0, 1.0); let pos = line.start + (line.end - line.start)
|
||||
// * t; return Some((t, pos, pos.distance_squared(point)));
|
||||
|
||||
// Quadratic
|
||||
|
||||
// let curve = QuadraticBezier2 {
|
||||
// start: line.x,
|
||||
// ctrl: eval_at(0.5),
|
||||
// end: line.y,
|
||||
// };
|
||||
// let (t, pos) = curve.binary_search_point_by_steps(point, 16, 0.001);
|
||||
// let t = t.clamped(0.0, 1.0);
|
||||
// let pos = curve.evaluate(t);
|
||||
// return Some((t, pos, pos.distance_squared(point)));
|
||||
|
||||
// Cubic
|
||||
|
||||
// let ctrl_at = |t: f64, end: f64| {
|
||||
// let a = eval_at(end);
|
||||
// let b = eval_at(Lerp::lerp(end, t, 0.1));
|
||||
// let dir = (b - a).normalized();
|
||||
// let exact = eval_at(t);
|
||||
// a + dir * exact.distance(a)
|
||||
// };
|
||||
// let curve = CubicBezier2 {
|
||||
// start: line.x,
|
||||
// ctrl0: ctrl_at(0.33, 0.0),
|
||||
// ctrl1: ctrl_at(0.66, 1.0),
|
||||
// end: line.y,
|
||||
// };
|
||||
// let (t, pos) = curve.binary_search_point_by_steps(point, 12, 0.01);
|
||||
// let t = t.clamped(0.0, 1.0);
|
||||
// let pos = curve.evaluate(t);
|
||||
// return Some((t, pos, pos.distance_squared(point)));
|
||||
|
||||
let a = spline.z.x;
|
||||
let b = spline.y.x;
|
||||
let c = spline.x.x;
|
||||
@ -328,18 +374,14 @@ pub fn quadratic_nearest_point(
|
||||
let min_root = roots
|
||||
.iter()
|
||||
.copied()
|
||||
.filter_map(|root| {
|
||||
.map(|root| {
|
||||
let river_point = spline.x * root * root + spline.y * root + spline.z;
|
||||
let river_zero = spline.z;
|
||||
let river_one = spline.x + spline.y + spline.z;
|
||||
if root > 0.0 && root < 1.0 {
|
||||
Some((root, river_point))
|
||||
} else if river_point.distance_squared(river_zero) < 0.5 {
|
||||
Some((root, /*river_point*/ river_zero))
|
||||
} else if river_point.distance_squared(river_one) < 0.5 {
|
||||
Some((root, /*river_point*/ river_one))
|
||||
(root, river_point)
|
||||
} else {
|
||||
None
|
||||
let root = root.clamped(0.0, 1.0);
|
||||
let river_point = spline.x * root * root + spline.y * root + spline.z;
|
||||
(root, river_point)
|
||||
}
|
||||
})
|
||||
.map(|(root, river_point)| {
|
||||
|
@ -31,7 +31,9 @@ impl Skeleton for ShipSkeleton {
|
||||
buf: &mut [FigureBoneData; super::MAX_BONE_COUNT],
|
||||
body: Self::Body,
|
||||
) -> Offsets {
|
||||
let bone0_mat = base_mat * Mat4::scaling_3d(1.0 / 11.0) * Mat4::<f32>::from(self.bone0);
|
||||
let scale_mat = Mat4::scaling_3d(1.0 / 11.0);
|
||||
|
||||
let bone0_mat = base_mat * scale_mat * Mat4::<f32>::from(self.bone0);
|
||||
|
||||
*(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [
|
||||
make_bone(bone0_mat),
|
||||
@ -43,10 +45,12 @@ impl Skeleton for ShipSkeleton {
|
||||
lantern: None,
|
||||
// TODO: see quadruped_medium for how to animate this
|
||||
mount_bone: Transform {
|
||||
position: common::comp::Body::Ship(body)
|
||||
.mountee_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
position: (base_mat * scale_mat).mul_point(
|
||||
common::comp::Body::Ship(body)
|
||||
.mountee_offset()
|
||||
.into_tuple()
|
||||
.into(),
|
||||
),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
@ -89,18 +93,22 @@ impl<'a> From<&'a Body> for SkeletonAttr {
|
||||
bone0: match body {
|
||||
DefaultAirship => (0.0, 0.0, 0.0),
|
||||
AirBalloon => (0.0, 0.0, 0.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
},
|
||||
bone1: match body {
|
||||
DefaultAirship => (-13.0, -25.0, 10.0),
|
||||
AirBalloon => (0.0, 0.0, 0.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
},
|
||||
bone2: match body {
|
||||
DefaultAirship => (13.0, -25.0, 10.0),
|
||||
AirBalloon => (0.0, 0.0, 0.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
},
|
||||
bone3: match body {
|
||||
DefaultAirship => (0.0, -27.5, 8.5),
|
||||
AirBalloon => (0.0, -9.0, 8.0),
|
||||
SailBoat => (0.0, 0.0, 0.0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ impl ForestKind {
|
||||
ForestKind::Cedar => 0.275..1.45,
|
||||
ForestKind::Pine => 0.2..1.4,
|
||||
ForestKind::Birch => 0.0..0.6,
|
||||
ForestKind::Mangrove => 0.65..1.3,
|
||||
ForestKind::Mangrove => 0.5..1.3,
|
||||
ForestKind::Swamp => 0.5..1.1,
|
||||
_ => 0.0..0.0,
|
||||
}
|
||||
@ -51,7 +51,7 @@ impl ForestKind {
|
||||
ForestKind::Cedar => -0.65..0.15,
|
||||
ForestKind::Pine => -1.8..-0.2,
|
||||
ForestKind::Birch => -0.7..0.25,
|
||||
ForestKind::Mangrove => 0.4..1.6,
|
||||
ForestKind::Mangrove => 0.35..1.6,
|
||||
ForestKind::Swamp => -0.6..0.8,
|
||||
_ => 0.0..0.0,
|
||||
}
|
||||
|
@ -147,23 +147,27 @@ impl<'a> BlockGen<'a> {
|
||||
} else {
|
||||
Some(Block::new(BlockKind::Earth, col))
|
||||
}
|
||||
} else if (wposf.z as f32) < height {
|
||||
} else if wposf.z as i32 <= height as i32 {
|
||||
let grass_factor = (wposf.z as f32 - (height - grass_depth))
|
||||
.div(grass_depth)
|
||||
.sqrt();
|
||||
let col = Lerp::lerp(sub_surface_color, surface_color, grass_factor);
|
||||
// Surface
|
||||
Some(Block::new(
|
||||
if snow_cover {
|
||||
Some(if water_level > height.ceil() {
|
||||
Block::new(
|
||||
BlockKind::Sand,
|
||||
sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||
)
|
||||
} else {
|
||||
let col = Lerp::lerp(sub_surface_color, surface_color, grass_factor);
|
||||
if grass_factor < 0.7 {
|
||||
Block::new(BlockKind::Earth, col.map(|e| (e * 255.0) as u8))
|
||||
} else if snow_cover {
|
||||
//if temp < CONFIG.snow_temp + 0.031 {
|
||||
BlockKind::Snow
|
||||
} else if grass_factor > 0.7 {
|
||||
BlockKind::Grass
|
||||
Block::new(BlockKind::Snow, col.map(|e| (e * 255.0) as u8))
|
||||
} else {
|
||||
BlockKind::Earth
|
||||
},
|
||||
col.map(|e| (e * 255.0) as u8),
|
||||
))
|
||||
Block::new(BlockKind::Grass, col.map(|e| (e * 255.0) as u8))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,12 @@
|
||||
pub mod scatter;
|
||||
pub mod shrub;
|
||||
pub mod spot;
|
||||
pub mod tree;
|
||||
pub mod wildlife;
|
||||
|
||||
pub use self::{scatter::apply_scatter_to, spot::apply_spots_to, tree::apply_trees_to};
|
||||
pub use self::{
|
||||
scatter::apply_scatter_to, shrub::apply_shrubs_to, spot::apply_spots_to, tree::apply_trees_to,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
column::ColumnSample,
|
||||
|
@ -9,7 +9,8 @@ fn close(x: f32, tgt: f32, falloff: f32) -> f32 {
|
||||
(1.0 - (x - tgt).abs() / falloff).max(0.0).powf(0.125)
|
||||
}
|
||||
|
||||
const MUSH_FACT: f32 = 1.0e-4; // To balance everything around the mushroom spawning rate
|
||||
const MUSH_FACT: f32 = 1.0e-4; // To balance things around the mushroom spawning rate
|
||||
const GRASS_FACT: f32 = 1.0e-3; // To balance things around the grass spawning rate
|
||||
const DEPTH_WATER_NORM: f32 = 15.0; // Water depth at which regular underwater sprites start spawning
|
||||
pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
use SpriteKind::*;
|
||||
@ -18,132 +19,132 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
let scatter: &[(
|
||||
_,
|
||||
bool,
|
||||
fn(&SimChunk, &ColumnSample) -> (f32, Option<(f32, f32)>),
|
||||
fn(&SimChunk, &ColumnSample) -> (f32, Option<(f32, f32, f32)>),
|
||||
)] = &[
|
||||
// (density, Option<(wavelen, threshold)>)
|
||||
// (density, Option<(base_density_proportion, wavelen, threshold)>)
|
||||
// Flowers
|
||||
(BlueFlower, false, |c, col| {
|
||||
(BlueFlower, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
col.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.4,
|
||||
)) * col.tree_density
|
||||
* MUSH_FACT
|
||||
* 256.0,
|
||||
Some((256.0, 0.25)),
|
||||
Some((0.0, 256.0, 0.25)),
|
||||
)
|
||||
}),
|
||||
(PinkFlower, false, |c, col| {
|
||||
(PinkFlower, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
Some((0.0, 100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(PurpleFlower, false, |c, col| {
|
||||
(PurpleFlower, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
col.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.4,
|
||||
)) * col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
Some((0.0, 100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(RedFlower, false, |c, col| {
|
||||
(RedFlower, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.tropical_temp, 0.7).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.tropical_temp, 0.7).min(close(
|
||||
col.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.4,
|
||||
)) * col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
Some((0.0, 100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(WhiteFlower, false, |c, col| {
|
||||
(WhiteFlower, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
Some((0.0, 100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(YellowFlower, false, |c, col| {
|
||||
(YellowFlower, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.1)),
|
||||
Some((0.0, 100.0, 0.1)),
|
||||
)
|
||||
}),
|
||||
(Cotton, false, |c, col| {
|
||||
(Cotton, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.temperate_temp, 0.7).min(close(
|
||||
col.humidity,
|
||||
CONFIG.jungle_hum,
|
||||
0.4,
|
||||
)) * col.tree_density
|
||||
* MUSH_FACT
|
||||
* 75.0,
|
||||
Some((256.0, 0.25)),
|
||||
Some((0.0, 256.0, 0.25)),
|
||||
)
|
||||
}),
|
||||
(Sunflower, false, |c, col| {
|
||||
(Sunflower, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 350.0,
|
||||
Some((100.0, 0.15)),
|
||||
Some((0.0, 100.0, 0.15)),
|
||||
)
|
||||
}),
|
||||
(WildFlax, false, |c, col| {
|
||||
(WildFlax, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.0, 0.7).min(close(c.humidity, CONFIG.jungle_hum, 0.4))
|
||||
close(col.temp, 0.0, 0.7).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* col.tree_density
|
||||
* MUSH_FACT
|
||||
* 600.0,
|
||||
Some((100.0, 0.15)),
|
||||
Some((0.0, 100.0, 0.15)),
|
||||
)
|
||||
}),
|
||||
// Herbs and Spices
|
||||
(LingonBerry, false, |c, _| {
|
||||
(LingonBerry, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.jungle_hum, 0.5))
|
||||
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.jungle_hum, 0.5))
|
||||
* MUSH_FACT
|
||||
* 2.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(LeafyPlant, false, |c, _| {
|
||||
(LeafyPlant, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.jungle_hum, 0.3))
|
||||
* MUSH_FACT
|
||||
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.jungle_hum, 0.3))
|
||||
* GRASS_FACT
|
||||
* 4.0,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(Fern, false, |c, _| {
|
||||
(Fern, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.5))
|
||||
* MUSH_FACT
|
||||
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.forest_hum, 0.5))
|
||||
* GRASS_FACT
|
||||
* 0.25,
|
||||
Some((64.0, 0.2)),
|
||||
Some((0.0, 64.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(Blueberry, false, |c, _| {
|
||||
(Blueberry, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.5).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.temperate_temp, 0.5).min(close(
|
||||
col.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.5,
|
||||
)) * MUSH_FACT
|
||||
@ -151,191 +152,200 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(Pumpkin, false, |c, _| {
|
||||
(Pumpkin, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.5).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.temperate_temp, 0.5).min(close(
|
||||
col.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.5,
|
||||
)) * MUSH_FACT
|
||||
* 500.0,
|
||||
Some((512.0, 0.05)),
|
||||
Some((0.0, 512.0, 0.05)),
|
||||
)
|
||||
}),
|
||||
// Collectable Objects
|
||||
// Only spawn twigs in temperate forests
|
||||
(Twigs, false, |c, _| {
|
||||
(Twigs, false, |_, col| {
|
||||
(
|
||||
(c.tree_density * 1.25 - 0.25).powf(0.5).max(0.0) * 0.75e-3,
|
||||
(col.tree_density * 1.25 - 0.25).powf(0.5).max(0.0) * 0.75e-3,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(Stones, false, |c, _| {
|
||||
((c.rockiness - 0.5).max(0.025) * 1.0e-3, None)
|
||||
(Stones, false, |chunk, _| {
|
||||
((chunk.rockiness - 0.5).max(0.025) * 1.0e-3, None)
|
||||
}),
|
||||
(Copper, false, |c, _| {
|
||||
((c.rockiness - 0.5).max(0.0) * 1.5e-3, None)
|
||||
(Copper, false, |chunk, _| {
|
||||
((chunk.rockiness - 0.5).max(0.0) * 1.5e-3, None)
|
||||
}),
|
||||
(Tin, false, |c, _| {
|
||||
((c.rockiness - 0.5).max(0.0) * 1.5e-3, None)
|
||||
(Tin, false, |chunk, _| {
|
||||
((chunk.rockiness - 0.5).max(0.0) * 1.5e-3, None)
|
||||
}),
|
||||
// Don't spawn Mushrooms in snowy regions
|
||||
(Mushroom, false, |c, _| {
|
||||
(Mushroom, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.4).min(close(c.humidity, CONFIG.forest_hum, 0.35)) * MUSH_FACT,
|
||||
close(col.temp, 0.3, 0.4).min(close(col.humidity, CONFIG.forest_hum, 0.35))
|
||||
* MUSH_FACT,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
// Grass
|
||||
(ShortGrass, false, |c, _| {
|
||||
(ShortGrass, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.2, 0.65).min(close(c.humidity, CONFIG.jungle_hum, 0.4)) * 0.015,
|
||||
None,
|
||||
close(col.temp, 0.2, 0.75).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* GRASS_FACT
|
||||
* 150.0,
|
||||
Some((0.3, 64.0, 0.3)),
|
||||
)
|
||||
}),
|
||||
(MediumGrass, false, |c, _| {
|
||||
(MediumGrass, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.2, 0.6).min(close(c.humidity, CONFIG.jungle_hum, 0.4)) * 0.012,
|
||||
None,
|
||||
close(col.temp, 0.2, 0.6).min(close(col.humidity, CONFIG.jungle_hum, 0.4))
|
||||
* GRASS_FACT
|
||||
* 120.0,
|
||||
Some((0.3, 64.0, 0.3)),
|
||||
)
|
||||
}),
|
||||
(LongGrass, false, |c, _| {
|
||||
(LongGrass, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 0.3, 0.35).min(close(c.humidity, CONFIG.jungle_hum, 0.3)) * 0.15,
|
||||
Some((48.0, 0.2)),
|
||||
close(col.temp, 0.3, 0.35).min(close(col.humidity, CONFIG.jungle_hum, 0.3))
|
||||
* GRASS_FACT
|
||||
* 150.0,
|
||||
Some((0.1, 48.0, 0.3)),
|
||||
)
|
||||
}),
|
||||
// Jungle Sprites
|
||||
// (LongGrass, false, |c, col| {
|
||||
// (
|
||||
// close(c.temp, CONFIG.tropical_temp, 0.4).min(close(
|
||||
// c.humidity,
|
||||
// close(col.temp, CONFIG.tropical_temp, 0.4).min(close(
|
||||
// col.humidity,
|
||||
// CONFIG.jungle_hum,
|
||||
// 0.6,
|
||||
// )) * 0.08,
|
||||
// Some((60.0, 5.0)),
|
||||
// Some((0.0, 60.0, 5.0)),
|
||||
// )
|
||||
// }),
|
||||
/*(WheatGreen, false, |c, col| {
|
||||
(
|
||||
close(c.temp, 0.4, 0.2).min(close(c.humidity, CONFIG.forest_hum, 0.1))
|
||||
close(col.temp, 0.4, 0.2).min(close(col.humidity, CONFIG.forest_hum, 0.1))
|
||||
* MUSH_FACT
|
||||
* 0.001,
|
||||
None,
|
||||
)
|
||||
}),*/
|
||||
(GrassSnow, false, |c, _| {
|
||||
(GrassSnow, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
|
||||
col.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.5,
|
||||
)) * 0.01,
|
||||
Some((48.0, 0.2)),
|
||||
)) * GRASS_FACT
|
||||
* 100.0,
|
||||
Some((0.0, 48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(Moonbell, false, |c, _| {
|
||||
(Moonbell, false, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
|
||||
c.humidity,
|
||||
close(col.temp, CONFIG.snow_temp - 0.2, 0.4).min(close(
|
||||
col.humidity,
|
||||
CONFIG.forest_hum,
|
||||
0.5,
|
||||
)) * 0.003,
|
||||
Some((48.0, 0.2)),
|
||||
Some((0.0, 48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
// Savanna Plants
|
||||
(SavannaGrass, false, |c, _| {
|
||||
(SavannaGrass, false, |_, col| {
|
||||
(
|
||||
{
|
||||
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
|
||||
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
|
||||
(savanna - desert * 5.0).max(0.0)
|
||||
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
|
||||
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
|
||||
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 250.0
|
||||
},
|
||||
Some((0.5, 0.2)),
|
||||
Some((0.15, 64.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(TallSavannaGrass, false, |c, _| {
|
||||
(TallSavannaGrass, false, |_, col| {
|
||||
(
|
||||
{
|
||||
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
|
||||
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
|
||||
(savanna - desert * 5.0).max(0.0)
|
||||
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
|
||||
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
|
||||
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 150.0
|
||||
},
|
||||
Some((12.5, 0.25)),
|
||||
Some((0.1, 48.0, 0.2)),
|
||||
)
|
||||
}),
|
||||
(RedSavannaGrass, false, |c, _| {
|
||||
(RedSavannaGrass, false, |_, col| {
|
||||
(
|
||||
{
|
||||
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
|
||||
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
|
||||
(savanna - desert * 5.0).max(0.0)
|
||||
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
|
||||
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
|
||||
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 120.0
|
||||
},
|
||||
Some((0.1, 0.1)),
|
||||
Some((0.15, 48.0, 0.25)),
|
||||
)
|
||||
}),
|
||||
(SavannaBush, false, |c, _| {
|
||||
(SavannaBush, false, |_, col| {
|
||||
(
|
||||
{
|
||||
let savanna = close(c.temp, 1.0, 0.4).min(close(c.humidity, 0.2, 0.25));
|
||||
let desert = close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1));
|
||||
(savanna - desert * 5.0).max(0.0)
|
||||
let savanna = close(col.temp, 1.0, 0.4) * close(col.humidity, 0.2, 0.25);
|
||||
let desert = close(col.temp, 1.0, 0.25) * close(col.humidity, 0.0, 0.1);
|
||||
(savanna - desert * 5.0).max(0.0) * GRASS_FACT * 40.0
|
||||
},
|
||||
Some((0.08, 0.05)),
|
||||
Some((0.1, 96.0, 0.15)),
|
||||
)
|
||||
}),
|
||||
// Desert Plants
|
||||
(DeadBush, false, |c, _| {
|
||||
(DeadBush, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.95).min(close(c.humidity, 0.0, 0.3)) * MUSH_FACT * 7.5,
|
||||
close(col.temp, 1.0, 0.95).min(close(col.humidity, 0.0, 0.3)) * MUSH_FACT * 7.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(Pyrebloom, false, |c, _| {
|
||||
(Pyrebloom, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.95).min(close(c.humidity, 0.0, 0.3)) * MUSH_FACT * 0.35,
|
||||
close(col.temp, 1.0, 0.95).min(close(col.humidity, 0.0, 0.3)) * MUSH_FACT * 0.35,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(LargeCactus, false, |c, _| {
|
||||
(LargeCactus, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 3.5,
|
||||
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 1.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(RoundCactus, false, |c, _| {
|
||||
(RoundCactus, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
|
||||
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(ShortCactus, false, |c, _| {
|
||||
(ShortCactus, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
|
||||
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(MedFlatCactus, false, |c, _| {
|
||||
(MedFlatCactus, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
|
||||
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(ShortFlatCactus, false, |c, _| {
|
||||
(ShortFlatCactus, false, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.25).min(close(c.humidity, 0.0, 0.1)) * MUSH_FACT * 5.5,
|
||||
close(col.temp, 1.0, 0.25).min(close(col.humidity, 0.0, 0.1)) * MUSH_FACT * 2.5,
|
||||
None,
|
||||
)
|
||||
}),
|
||||
(Reed, false, |c, col| {
|
||||
(Reed, false, |_, col| {
|
||||
(
|
||||
close(c.humidity, CONFIG.jungle_hum, 0.7)
|
||||
close(col.humidity, CONFIG.jungle_hum, 0.9)
|
||||
* col
|
||||
.water_dist
|
||||
.map(|wd| Lerp::lerp(0.2, 0.0, (wd / 8.0).clamped(0.0, 1.0)))
|
||||
.unwrap_or(0.0),
|
||||
Some((128.0, 0.5)),
|
||||
.unwrap_or(0.0)
|
||||
* ((col.alt - CONFIG.sea_level) / 12.0).clamped(0.0, 1.0),
|
||||
Some((0.2, 128.0, 0.5)),
|
||||
)
|
||||
}),
|
||||
// Underwater chests
|
||||
@ -374,23 +384,23 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((100.0, 0.15)),
|
||||
Some((0.0, 100.0, 0.15)),
|
||||
)
|
||||
}),
|
||||
// seagrass
|
||||
(Seagrass, true, |c, col| {
|
||||
(Seagrass, true, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.8)
|
||||
close(col.temp, CONFIG.temperate_temp, 0.8)
|
||||
* MUSH_FACT
|
||||
* 300.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 18.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((150.0, 0.3)),
|
||||
Some((0.0, 150.0, 0.3)),
|
||||
)
|
||||
}),
|
||||
// seagrass, coastal patches
|
||||
@ -398,44 +408,44 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
(
|
||||
MUSH_FACT
|
||||
* 600.0
|
||||
* if col.water_level < CONFIG.sea_level && (col.water_level - col.alt) < 3.0 {
|
||||
* if col.water_level <= CONFIG.sea_level && (col.water_level - col.alt) < 3.0 {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((150.0, 0.4)),
|
||||
Some((0.0, 150.0, 0.4)),
|
||||
)
|
||||
}),
|
||||
// scattered seaweed (temperate species)
|
||||
(SeaweedTemperate, true, |c, col| {
|
||||
(SeaweedTemperate, true, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.8)
|
||||
close(col.temp, CONFIG.temperate_temp, 0.8)
|
||||
* MUSH_FACT
|
||||
* 50.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 11.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((500.0, 0.75)),
|
||||
Some((0.0, 500.0, 0.75)),
|
||||
)
|
||||
}),
|
||||
// scattered seaweed (tropical species)
|
||||
(SeaweedTropical, true, |c, col| {
|
||||
(SeaweedTropical, true, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.95)
|
||||
close(col.temp, 1.0, 0.95)
|
||||
* MUSH_FACT
|
||||
* 50.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 11.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((500.0, 0.75)),
|
||||
Some((0.0, 500.0, 0.75)),
|
||||
)
|
||||
}),
|
||||
// Caulerpa lentillifera algae patch
|
||||
@ -443,14 +453,14 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
(
|
||||
MUSH_FACT
|
||||
* 250.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((100.0, 0.15)),
|
||||
Some((0.0, 100.0, 0.15)),
|
||||
)
|
||||
}),
|
||||
// Caulerpa prolifera algae patch
|
||||
@ -458,110 +468,110 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
(
|
||||
MUSH_FACT
|
||||
* 250.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((100.0, 0.15)),
|
||||
Some((0.0, 100.0, 0.15)),
|
||||
)
|
||||
}),
|
||||
// Mermaids' fan algae patch
|
||||
(MermaidsFan, true, |c, col| {
|
||||
(MermaidsFan, true, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.95)
|
||||
close(col.temp, 1.0, 0.95)
|
||||
* MUSH_FACT
|
||||
* 500.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((50.0, 0.10)),
|
||||
Some((0.0, 50.0, 0.10)),
|
||||
)
|
||||
}),
|
||||
// Sea anemones
|
||||
(SeaAnemone, true, |c, col| {
|
||||
(SeaAnemone, true, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.8)
|
||||
close(col.temp, CONFIG.temperate_temp, 0.8)
|
||||
* MUSH_FACT
|
||||
* 125.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM - 9.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((100.0, 0.3)),
|
||||
Some((0.0, 100.0, 0.3)),
|
||||
)
|
||||
}),
|
||||
// Giant Kelp
|
||||
(GiantKelp, true, |c, col| {
|
||||
(GiantKelp, true, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.8)
|
||||
close(col.temp, CONFIG.temperate_temp, 0.8)
|
||||
* MUSH_FACT
|
||||
* 220.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM - 9.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((200.0, 0.4)),
|
||||
Some((0.0, 200.0, 0.4)),
|
||||
)
|
||||
}),
|
||||
// Bull Kelp
|
||||
(BullKelp, true, |c, col| {
|
||||
(BullKelp, true, |_, col| {
|
||||
(
|
||||
close(c.temp, CONFIG.temperate_temp, 0.7)
|
||||
close(col.temp, CONFIG.temperate_temp, 0.7)
|
||||
* MUSH_FACT
|
||||
* 300.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 3.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((75.0, 0.3)),
|
||||
Some((0.0, 75.0, 0.3)),
|
||||
)
|
||||
}),
|
||||
// Stony Corals
|
||||
(StonyCoral, true, |c, col| {
|
||||
(StonyCoral, true, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.9)
|
||||
close(col.temp, 1.0, 0.9)
|
||||
* MUSH_FACT
|
||||
* 160.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((120.0, 0.4)),
|
||||
Some((0.0, 120.0, 0.4)),
|
||||
)
|
||||
}),
|
||||
// Soft Corals
|
||||
(SoftCoral, true, |c, col| {
|
||||
(SoftCoral, true, |_, col| {
|
||||
(
|
||||
close(c.temp, 1.0, 0.9)
|
||||
close(col.temp, 1.0, 0.9)
|
||||
* MUSH_FACT
|
||||
* 120.0
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 10.0
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
Some((120.0, 0.4)),
|
||||
Some((0.0, 120.0, 0.4)),
|
||||
)
|
||||
}),
|
||||
// Seashells
|
||||
@ -569,7 +579,7 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
(
|
||||
(c.rockiness - 0.5).max(0.0)
|
||||
* 1.0e-3
|
||||
* if col.water_level < CONFIG.sea_level
|
||||
* if col.water_level <= CONFIG.sea_level
|
||||
&& col.alt < col.water_level - DEPTH_WATER_NORM + 20.0
|
||||
{
|
||||
1.0
|
||||
@ -594,16 +604,16 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
];
|
||||
|
||||
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||
let underwater = col.water_level > col.alt;
|
||||
let underwater = col.water_level.floor() > col.alt;
|
||||
|
||||
let kind = scatter
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, (kind, is_underwater, f))| {
|
||||
let (density, patch) = f(canvas.chunk(), col);
|
||||
let is_patch = patch
|
||||
.map(|(wavelen, threshold)| {
|
||||
canvas
|
||||
let density = patch
|
||||
.map(|(base_density_prop, wavelen, threshold)| {
|
||||
if canvas
|
||||
.index()
|
||||
.noise
|
||||
.scatter_nz
|
||||
@ -614,10 +624,14 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
)
|
||||
.abs()
|
||||
> 1.0 - threshold as f64
|
||||
{
|
||||
density
|
||||
} else {
|
||||
density * base_density_prop
|
||||
}
|
||||
})
|
||||
.unwrap_or(true);
|
||||
.unwrap_or(density);
|
||||
if density > 0.0
|
||||
&& is_patch
|
||||
&& rng.gen::<f32>() < density //RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|
||||
&& underwater == *is_underwater
|
||||
{
|
||||
|
76
world/src/layer/shrub.rs
Normal file
76
world/src/layer/shrub.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
util::{seed_expan, Sampler, StructureGen2d, UnitChooser},
|
||||
Canvas,
|
||||
};
|
||||
use common::{
|
||||
assets::AssetHandle,
|
||||
terrain::structure::{Structure, StructuresGroup},
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use vek::*;
|
||||
|
||||
lazy_static! {
|
||||
static ref JUNGLE_SHRUBS: AssetHandle<StructuresGroup> = Structure::load_group("shrubs.jungle");
|
||||
}
|
||||
|
||||
struct Shrub {
|
||||
wpos: Vec3<i32>,
|
||||
seed: u32,
|
||||
kind: ForestKind,
|
||||
}
|
||||
|
||||
pub fn apply_shrubs_to(canvas: &mut Canvas, _dynamic_rng: &mut impl Rng) {
|
||||
let mut shrub_cache = HashMap::new();
|
||||
|
||||
let shrub_gen = StructureGen2d::new(canvas.index().seed, 8, 4);
|
||||
|
||||
let info = canvas.info();
|
||||
canvas.foreach_col(|_, wpos2d, _| {
|
||||
for (wpos, seed) in std::array::IntoIter::new(shrub_gen.get(wpos2d)) {
|
||||
shrub_cache.entry(wpos).or_insert_with(|| {
|
||||
let col = info.col_or_gen(wpos)?;
|
||||
|
||||
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(seed));
|
||||
|
||||
const BASE_SHRUB_DENSITY: f64 = 0.15;
|
||||
if rng.gen_bool((BASE_SHRUB_DENSITY * col.tree_density as f64).clamped(0.0, 1.0))
|
||||
&& col.water_dist.map_or(true, |d| d > 8.0)
|
||||
&& col.alt > col.water_level
|
||||
&& col.spawn_rate > 0.9
|
||||
&& col.path.map_or(true, |(d, _, _, _)| d > 6.0)
|
||||
{
|
||||
Some(Shrub {
|
||||
wpos: wpos.with_z(col.alt as i32),
|
||||
seed,
|
||||
kind: *info
|
||||
.chunks()
|
||||
.make_forest_lottery(wpos)
|
||||
.choose_seeded(seed)
|
||||
.as_ref()?,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
for shrub in shrub_cache.values().filter_map(|s| s.as_ref()) {
|
||||
let mut rng = ChaChaRng::from_seed(seed_expan::rng_state(shrub.seed));
|
||||
|
||||
let units = UnitChooser::new(shrub.seed).get(shrub.seed).into();
|
||||
|
||||
let shrubs = match shrub.kind {
|
||||
ForestKind::Mangrove => &JUNGLE_SHRUBS,
|
||||
_ => continue, // TODO: Add more shrub varieties
|
||||
}
|
||||
.read();
|
||||
|
||||
let structure = shrubs.choose(&mut rng).unwrap();
|
||||
canvas.blit_structure(shrub.wpos, structure, shrub.seed, units, true);
|
||||
}
|
||||
}
|
@ -360,7 +360,7 @@ impl TreeConfig {
|
||||
}
|
||||
|
||||
pub fn jungle(rng: &mut impl Rng, scale: f32) -> Self {
|
||||
let scale = scale * (0.9 + rng.gen::<f32>().powi(4) * 1.0);
|
||||
let scale = scale * (0.8 + rng.gen::<f32>() * 0.5);
|
||||
let log_scale = 1.0 + scale.log2().max(0.0);
|
||||
|
||||
Self {
|
||||
@ -369,14 +369,14 @@ impl TreeConfig {
|
||||
branch_child_len: 0.35,
|
||||
branch_child_radius: 0.5,
|
||||
branch_child_radius_lerp: true,
|
||||
leaf_radius: 4.0 * log_scale..4.5 * log_scale,
|
||||
leaf_radius_scaled: 0.0,
|
||||
leaf_radius: 10.0 * log_scale..11.5 * log_scale,
|
||||
leaf_radius_scaled: -8.0 * log_scale,
|
||||
straightness: 0.2,
|
||||
max_depth: 2,
|
||||
splits: 7.5..8.5,
|
||||
split_range: 0.3..1.25,
|
||||
split_range: 0.2..1.25,
|
||||
branch_len_bias: 0.5,
|
||||
leaf_vertical_scale: 0.4,
|
||||
leaf_vertical_scale: 0.35,
|
||||
proportionality: 0.8,
|
||||
inhabited: false,
|
||||
hanging_sprites: &[(0.00007, SpriteKind::Beehive), (0.015, SpriteKind::Liana)],
|
||||
@ -467,16 +467,16 @@ impl TreeConfig {
|
||||
trunk_len: 13.0 * scale,
|
||||
trunk_radius: 1.65 * scale,
|
||||
branch_child_len: 0.75,
|
||||
branch_child_radius: 0.75,
|
||||
branch_child_radius: 0.6,
|
||||
branch_child_radius_lerp: true,
|
||||
leaf_radius: 2.0 * log_scale..2.1 * log_scale,
|
||||
leaf_radius: 1.5 * log_scale..2.0 * log_scale,
|
||||
leaf_radius_scaled: 0.0,
|
||||
straightness: 0.3,
|
||||
max_depth: 5,
|
||||
splits: 3.5..4.25,
|
||||
split_range: 0.5..1.25,
|
||||
branch_len_bias: 0.0,
|
||||
leaf_vertical_scale: 0.5,
|
||||
leaf_vertical_scale: 0.65,
|
||||
proportionality: 0.5,
|
||||
inhabited: false,
|
||||
hanging_sprites: &[(0.00007, SpriteKind::Beehive)],
|
||||
|
@ -347,6 +347,7 @@ impl World {
|
||||
};
|
||||
|
||||
layer::apply_caves_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_trees_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
|
||||
layer::apply_paths_to(&mut canvas);
|
||||
|
@ -1563,8 +1563,10 @@ impl WorldSim {
|
||||
let dist = rpos.map(|e| e as f32).magnitude();
|
||||
if let Some(c) = self.get_mut(cliff + rpos) {
|
||||
let warp = 1.0 / (1.0 + dist);
|
||||
c.tree_density *= 1.0 - warp;
|
||||
c.cliff_height = Lerp::lerp(44.0, 0.0, -1.0 + dist / 3.5);
|
||||
if !c.river.near_water() {
|
||||
c.tree_density *= 1.0 - warp;
|
||||
c.cliff_height = Lerp::lerp(44.0, 0.0, -1.0 + dist / 3.5);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -2054,47 +2056,45 @@ impl WorldSim {
|
||||
self.get_nearest_way(wpos, |chunk| Some(chunk.cave))
|
||||
}
|
||||
|
||||
/// Create a [`Lottery<Option<ForestKind>>`] that generates [`ForestKind`]s
|
||||
/// according to the conditions at the given position. If no or fewer
|
||||
/// trees are appropriate for the conditions, `None` may be generated.
|
||||
pub fn make_forest_lottery(&self, wpos: Vec2<i32>) -> Lottery<Option<ForestKind>> {
|
||||
let chunk = if let Some(chunk) = self.get_wpos(wpos) {
|
||||
chunk
|
||||
} else {
|
||||
return Lottery::from(vec![(1.0, None)]);
|
||||
};
|
||||
let env = chunk.get_environment();
|
||||
Lottery::from(
|
||||
ForestKind::into_enum_iter()
|
||||
.enumerate()
|
||||
.map(|(i, fk)| {
|
||||
const CLUSTER_SIZE: f64 = 48.0;
|
||||
let nz = (FastNoise2d::new(i as u32 * 37)
|
||||
.get(wpos.map(|e| e as f64) / CLUSTER_SIZE)
|
||||
+ 1.0)
|
||||
/ 2.0;
|
||||
(fk.proclivity(&env) * nz, Some(fk))
|
||||
})
|
||||
.chain(std::iter::once((0.001, None)))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Return an iterator over candidate tree positions (note that only some of
|
||||
/// these will become trees since environmental parameters may forbid
|
||||
/// them spawning).
|
||||
pub fn get_near_trees(&self, wpos: Vec2<i32>) -> impl Iterator<Item = TreeAttr> + '_ {
|
||||
// Deterministic based on wpos
|
||||
let normal_trees = std::array::IntoIter::new(self.gen_ctx.structure_gen.get(wpos))
|
||||
.filter_map(move |(pos, seed)| {
|
||||
let chunk = self.get_wpos(pos)?;
|
||||
let env = Environment {
|
||||
humid: chunk.humidity,
|
||||
temp: chunk.temp,
|
||||
near_water: if chunk.river.is_lake()
|
||||
|| chunk.river.near_river()
|
||||
|| chunk.alt < CONFIG.sea_level + 6.0
|
||||
// Close to sea in altitude
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
};
|
||||
.filter_map(move |(wpos, seed)| {
|
||||
let lottery = self.make_forest_lottery(wpos);
|
||||
Some(TreeAttr {
|
||||
pos,
|
||||
pos: wpos,
|
||||
seed,
|
||||
scale: 1.0,
|
||||
forest_kind: *Lottery::from(
|
||||
ForestKind::into_enum_iter()
|
||||
.enumerate()
|
||||
.map(|(i, fk)| {
|
||||
const CLUSTER_SIZE: f64 = 48.0;
|
||||
let nz = (FastNoise2d::new(i as u32 * 37)
|
||||
.get(pos.map(|e| e as f64) / CLUSTER_SIZE)
|
||||
+ 1.0)
|
||||
/ 2.0;
|
||||
(fk.proclivity(&env) * nz, Some(fk))
|
||||
})
|
||||
.chain(std::iter::once((0.001, None)))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.choose_seeded(seed)
|
||||
.as_ref()?,
|
||||
forest_kind: *lottery.choose_seeded(seed).as_ref()?,
|
||||
inhabited: false,
|
||||
})
|
||||
});
|
||||
@ -2219,7 +2219,8 @@ impl SimChunk {
|
||||
} else {
|
||||
Some(
|
||||
uniform_idx_as_vec2(map_size_lg, downhill_pre as usize)
|
||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||
* TerrainChunkSize::RECT_SIZE.map(|e| e as i32)
|
||||
+ TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2),
|
||||
)
|
||||
};
|
||||
|
||||
@ -2295,10 +2296,10 @@ impl SimChunk {
|
||||
.min(1.0);
|
||||
|
||||
// Add geologically short timescale undulation to the world for various reasons
|
||||
let alt = alt
|
||||
let alt =
|
||||
// Don't add undulation to rivers, mainly because this could accidentally result in rivers flowing uphill
|
||||
+ if river.near_water() {
|
||||
0.0
|
||||
if river.near_water() {
|
||||
alt
|
||||
} else {
|
||||
// Sand dunes (formed over a short period of time, so we don't care about erosion sim)
|
||||
let warp = Vec2::new(
|
||||
@ -2323,7 +2324,16 @@ impl SimChunk {
|
||||
const SOIL_SCALE: f32 = 16.0;
|
||||
let soil = soil_nz * SOIL_SCALE * tree_density.sqrt() * humidity.sqrt();
|
||||
|
||||
dune + soil
|
||||
let warp_factor = ((alt - CONFIG.sea_level) / 16.0).clamped(0.0, 1.0);
|
||||
|
||||
let warp = (dune + soil) * warp_factor;
|
||||
|
||||
// Prevent warping pushing the altitude underwater
|
||||
if alt + warp < water_alt {
|
||||
alt
|
||||
} else {
|
||||
alt + warp
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
@ -2415,4 +2425,20 @@ impl SimChunk {
|
||||
}
|
||||
|
||||
pub fn near_cliffs(&self) -> bool { self.cliff_height > 0.0 }
|
||||
|
||||
pub fn get_environment(&self) -> Environment {
|
||||
Environment {
|
||||
humid: self.humidity,
|
||||
temp: self.temp,
|
||||
near_water: if self.river.is_lake()
|
||||
|| self.river.near_river()
|
||||
|| self.alt < CONFIG.sea_level + 6.0
|
||||
// Close to sea in altitude
|
||||
{
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user