mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add more crops
This commit is contained in:
parent
7d4443cbcd
commit
0fb3a115da
@ -17,7 +17,7 @@ opt-level = 2
|
|||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
debug = true
|
debug = false
|
||||||
codegen-units = 8
|
codegen-units = 8
|
||||||
lto = false
|
lto = false
|
||||||
incremental = true
|
incremental = true
|
||||||
|
BIN
assets/voxygen/background/bg_10.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_10.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/background/bg_11.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/background/bg_11.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cabbage/cabbage-0.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/sprite/cabbage/cabbage-0.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cabbage/cabbage-1.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/sprite/cabbage/cabbage-1.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/cabbage/cabbage-2.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/sprite/cabbage/cabbage-2.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/carrot/0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/carrot/0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/carrot/1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/carrot/1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/carrot/2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/carrot/2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/carrot/3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/carrot/3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/carrot/4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/carrot/4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/carrot/5.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/carrot/5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/flax/flax-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/flax/flax-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/flax/flax-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/flax/flax-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/flax/flax-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/flax/flax-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/flax/flax-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/flax/flax-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/flax/flax-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/flax/flax-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/flax/flax-5.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/flax/flax-5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/radish/0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/radish/0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/radish/1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/radish/1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/radish/2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/radish/2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/radish/3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/radish/3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/radish/4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/radish/4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/tomato/0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/tomato/0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/tomato/1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/tomato/1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/tomato/2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/tomato/2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/tomato/3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/tomato/3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/tomato/4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/tomato/4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/turnip/turnip-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/turnip/turnip-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/turnip/turnip-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/turnip/turnip-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/turnip/turnip-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/turnip/turnip-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/turnip/turnip-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/turnip/turnip-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/turnip/turnip-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/turnip/turnip-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/turnip/turnip-5.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/turnip/turnip-5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -139,9 +139,7 @@ impl<S: Clone + Eq + Hash> Astar<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cheapest_cost(&self) -> Option<f32> {
|
pub fn get_cheapest_cost(&self) -> Option<f32> { self.cheapest_cost }
|
||||||
self.cheapest_cost
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reconstruct_path_to(&mut self, end: S) -> Path<S> {
|
fn reconstruct_path_to(&mut self, end: S) -> Path<S> {
|
||||||
let mut path = vec![end.clone()];
|
let mut path = vec![end.clone()];
|
||||||
|
@ -47,7 +47,5 @@ pub struct ChunkSupplement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ChunkSupplement {
|
impl ChunkSupplement {
|
||||||
pub fn add_entity(&mut self, entity: EntityInfo) {
|
pub fn add_entity(&mut self, entity: EntityInfo) { self.entities.push(entity); }
|
||||||
self.entities.push(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -28,13 +28,13 @@ pub mod region;
|
|||||||
pub mod spiral;
|
pub mod spiral;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
pub mod states;
|
pub mod states;
|
||||||
|
pub mod store;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
pub mod terrain;
|
pub mod terrain;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod vol;
|
pub mod vol;
|
||||||
pub mod volumes;
|
pub mod volumes;
|
||||||
pub mod store;
|
|
||||||
|
|
||||||
/// The networking module containing high-level wrappers of `TcpListener` and
|
/// The networking module containing high-level wrappers of `TcpListener` and
|
||||||
/// `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by
|
/// `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by
|
||||||
|
@ -26,9 +26,7 @@ impl<T> fmt::Debug for Id<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> hash::Hash for Id<T> {
|
impl<T> hash::Hash for Id<T> {
|
||||||
fn hash<H: hash::Hasher>(&self, h: &mut H) {
|
fn hash<H: hash::Hasher>(&self, h: &mut H) { self.0.hash(h); }
|
||||||
self.0.hash(h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Store<T> {
|
pub struct Store<T> {
|
||||||
@ -53,8 +51,7 @@ impl<T> Store<T> {
|
|||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> { self.items.iter_mut() }
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> { self.items.iter_mut() }
|
||||||
|
|
||||||
pub fn iter_ids(&self) -> impl Iterator<Item = (Id<T>, &T)> {
|
pub fn iter_ids(&self) -> impl Iterator<Item = (Id<T>, &T)> {
|
||||||
self
|
self.items
|
||||||
.items
|
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, item)| (Id(i, PhantomData), item))
|
.map(|(i, item)| (Id(i, PhantomData), item))
|
||||||
|
@ -45,6 +45,10 @@ pub enum BlockKind {
|
|||||||
WheatYellow,
|
WheatYellow,
|
||||||
WheatGreen,
|
WheatGreen,
|
||||||
Cabbage,
|
Cabbage,
|
||||||
|
Flax,
|
||||||
|
Carrot,
|
||||||
|
Tomato,
|
||||||
|
Radish,
|
||||||
Coconut,
|
Coconut,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +95,12 @@ impl BlockKind {
|
|||||||
BlockKind::Corn => true,
|
BlockKind::Corn => true,
|
||||||
BlockKind::WheatYellow => true,
|
BlockKind::WheatYellow => true,
|
||||||
BlockKind::WheatGreen => true,
|
BlockKind::WheatGreen => true,
|
||||||
BlockKind::Cabbage => true,
|
BlockKind::Cabbage => false,
|
||||||
|
BlockKind::Pumpkin => false,
|
||||||
|
BlockKind::Flax => true,
|
||||||
|
BlockKind::Carrot => true,
|
||||||
|
BlockKind::Tomato => false,
|
||||||
|
BlockKind::Radish => true,
|
||||||
BlockKind::Coconut => true,
|
BlockKind::Coconut => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -142,6 +151,10 @@ impl BlockKind {
|
|||||||
BlockKind::WheatYellow => false,
|
BlockKind::WheatYellow => false,
|
||||||
BlockKind::WheatGreen => false,
|
BlockKind::WheatGreen => false,
|
||||||
BlockKind::Cabbage => false,
|
BlockKind::Cabbage => false,
|
||||||
|
BlockKind::Flax => false,
|
||||||
|
BlockKind::Carrot => false,
|
||||||
|
BlockKind::Tomato => false,
|
||||||
|
BlockKind::Radish => false,
|
||||||
BlockKind::Coconut => false,
|
BlockKind::Coconut => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
@ -171,7 +184,7 @@ impl BlockKind {
|
|||||||
BlockKind::Mushroom => false,
|
BlockKind::Mushroom => false,
|
||||||
BlockKind::Liana => false,
|
BlockKind::Liana => false,
|
||||||
BlockKind::Chest => true,
|
BlockKind::Chest => true,
|
||||||
BlockKind::Pumpkin => true,
|
BlockKind::Pumpkin => false,
|
||||||
BlockKind::Welwitch => false,
|
BlockKind::Welwitch => false,
|
||||||
BlockKind::LingonBerry => false,
|
BlockKind::LingonBerry => false,
|
||||||
BlockKind::LeafyPlant => false,
|
BlockKind::LeafyPlant => false,
|
||||||
@ -183,6 +196,10 @@ impl BlockKind {
|
|||||||
BlockKind::WheatYellow => false,
|
BlockKind::WheatYellow => false,
|
||||||
BlockKind::WheatGreen => false,
|
BlockKind::WheatGreen => false,
|
||||||
BlockKind::Cabbage => false,
|
BlockKind::Cabbage => false,
|
||||||
|
BlockKind::Flax => false,
|
||||||
|
BlockKind::Carrot => false,
|
||||||
|
BlockKind::Tomato => true,
|
||||||
|
BlockKind::Radish => false,
|
||||||
BlockKind::Coconut => false,
|
BlockKind::Coconut => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
@ -205,7 +222,6 @@ impl BlockKind {
|
|||||||
BlockKind::Velorite => true,
|
BlockKind::Velorite => true,
|
||||||
BlockKind::VeloriteFrag => true,
|
BlockKind::VeloriteFrag => true,
|
||||||
BlockKind::Chest => true,
|
BlockKind::Chest => true,
|
||||||
BlockKind::Pumpkin => true,
|
|
||||||
BlockKind::Coconut => true,
|
BlockKind::Coconut => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -177,6 +177,8 @@ impl MainMenuUi {
|
|||||||
"voxygen.background.bg_7",
|
"voxygen.background.bg_7",
|
||||||
"voxygen.background.bg_8",
|
"voxygen.background.bg_8",
|
||||||
"voxygen.background.bg_9",
|
"voxygen.background.bg_9",
|
||||||
|
"voxygen.background.bg_10",
|
||||||
|
"voxygen.background.bg_11",
|
||||||
];
|
];
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
@ -88,7 +88,9 @@ impl Meshable<SpritePipeline, SpritePipeline> for Segment {
|
|||||||
SpriteVertex::new(
|
SpriteVertex::new(
|
||||||
origin,
|
origin,
|
||||||
norm,
|
norm,
|
||||||
linear_to_srgb(srgb_to_linear(col) * light.min(ao.powf(0.5) * 0.75 + 0.25)),
|
linear_to_srgb(
|
||||||
|
srgb_to_linear(col) * light.min(ao.powf(0.5) * 0.75 + 0.25),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
&{
|
&{
|
||||||
|
@ -197,11 +197,13 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let pos = wpos - outer.min;
|
let pos = wpos - outer.min;
|
||||||
Some(light_map
|
Some(
|
||||||
|
light_map
|
||||||
.get(lm_idx(pos.x, pos.y, pos.z))
|
.get(lm_idx(pos.x, pos.y, pos.z))
|
||||||
.filter(|l| **l != OPAQUE && **l != UNKNOWN)
|
.filter(|l| **l != OPAQUE && **l != UNKNOWN)
|
||||||
.map(|l| *l as f32 / SUNLIGHT as f32)
|
.map(|l| *l as f32 / SUNLIGHT as f32)
|
||||||
.unwrap_or(0.0))
|
.unwrap_or(0.0),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,12 @@ fn get_ao_quad(
|
|||||||
for x in 0..2 {
|
for x in 0..2 {
|
||||||
for y in 0..2 {
|
for y in 0..2 {
|
||||||
let dark_pos = shift + offs[0] * x + offs[1] * y + 1;
|
let dark_pos = shift + offs[0] * x + offs[1] * y + 1;
|
||||||
if let Some(dark) = unsafe { darknesses
|
if let Some(dark) = unsafe {
|
||||||
|
darknesses
|
||||||
.get_unchecked(dark_pos.z as usize)
|
.get_unchecked(dark_pos.z as usize)
|
||||||
.get_unchecked(dark_pos.y as usize)
|
.get_unchecked(dark_pos.y as usize)
|
||||||
.get_unchecked(dark_pos.x as usize) }
|
.get_unchecked(dark_pos.x as usize)
|
||||||
{
|
} {
|
||||||
darkness += dark;
|
darkness += dark;
|
||||||
total += 1.0;
|
total += 1.0;
|
||||||
}
|
}
|
||||||
|
@ -240,7 +240,10 @@ impl Scene {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Tick camera for interpolation.
|
// Tick camera for interpolation.
|
||||||
self.camera.update(scene_data.state.get_time(), scene_data.state.get_delta_time());
|
self.camera.update(
|
||||||
|
scene_data.state.get_time(),
|
||||||
|
scene_data.state.get_delta_time(),
|
||||||
|
);
|
||||||
|
|
||||||
// Compute camera matrices.
|
// Compute camera matrices.
|
||||||
self.camera.compute_dependents(&*scene_data.state.terrain());
|
self.camera.compute_dependents(&*scene_data.state.terrain());
|
||||||
|
@ -190,6 +190,22 @@ fn sprite_config_for(kind: BlockKind) -> Option<SpriteConfig> {
|
|||||||
variations: 3,
|
variations: 3,
|
||||||
wind_sway: 0.0,
|
wind_sway: 0.0,
|
||||||
}),
|
}),
|
||||||
|
BlockKind::Flax => Some(SpriteConfig {
|
||||||
|
variations: 6,
|
||||||
|
wind_sway: 0.4,
|
||||||
|
}),
|
||||||
|
BlockKind::Carrot => Some(SpriteConfig {
|
||||||
|
variations: 6,
|
||||||
|
wind_sway: 0.1,
|
||||||
|
}),
|
||||||
|
BlockKind::Tomato => Some(SpriteConfig {
|
||||||
|
variations: 5,
|
||||||
|
wind_sway: 0.0,
|
||||||
|
}),
|
||||||
|
BlockKind::Radish => Some(SpriteConfig {
|
||||||
|
variations: 5,
|
||||||
|
wind_sway: 0.1,
|
||||||
|
}),
|
||||||
BlockKind::Coconut => Some(SpriteConfig {
|
BlockKind::Coconut => Some(SpriteConfig {
|
||||||
variations: 1,
|
variations: 1,
|
||||||
wind_sway: 0.0,
|
wind_sway: 0.0,
|
||||||
@ -1301,6 +1317,147 @@ impl<V: RectRasterableVol> Terrain<V> {
|
|||||||
Vec3::new(-6.0, -6.0, 0.0),
|
Vec3::new(-6.0, -6.0, 0.0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Flax
|
||||||
|
(
|
||||||
|
(BlockKind::Flax, 0),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.flax.flax-0",
|
||||||
|
Vec3::new(-6.0, -6.0, 0.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Flax, 1),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.flax.flax-1",
|
||||||
|
Vec3::new(-6.0, -6.0, 0.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Flax, 2),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.flax.flax-2",
|
||||||
|
Vec3::new(-6.0, -6.0, 0.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Flax, 3),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.flax.flax-3",
|
||||||
|
Vec3::new(-6.0, -6.0, 0.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Flax, 4),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.flax.flax-4",
|
||||||
|
Vec3::new(-6.0, -6.0, 0.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Flax, 5),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.flax.flax-5",
|
||||||
|
Vec3::new(-6.0, -6.0, 0.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Carrot
|
||||||
|
(
|
||||||
|
(BlockKind::Carrot, 0),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.carrot.0",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Carrot, 1),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.carrot.1",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Carrot, 2),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.carrot.2",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Carrot, 3),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.carrot.3",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Carrot, 4),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.carrot.4",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Carrot, 5),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.carrot.5",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Tomato, 0),
|
||||||
|
make_model("voxygen.voxel.sprite.tomato.0", Vec3::new(-5.5, -5.5, 0.0)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Tomato, 1),
|
||||||
|
make_model("voxygen.voxel.sprite.tomato.1", Vec3::new(-5.5, -5.5, 0.0)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Tomato, 2),
|
||||||
|
make_model("voxygen.voxel.sprite.tomato.2", Vec3::new(-5.5, -5.5, 0.0)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Tomato, 3),
|
||||||
|
make_model("voxygen.voxel.sprite.tomato.3", Vec3::new(-5.5, -5.5, 0.0)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Tomato, 4),
|
||||||
|
make_model("voxygen.voxel.sprite.tomato.4", Vec3::new(-5.5, -5.5, 0.0)),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Radish, 0),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.radish.0",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Radish, 1),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.radish.1",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Radish, 2),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.radish.2",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Radish, 3),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.radish.3",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(BlockKind::Radish, 4),
|
||||||
|
make_model(
|
||||||
|
"voxygen.voxel.sprite.radish.4",
|
||||||
|
Vec3::new(-5.5, -5.5, -0.25),
|
||||||
|
),
|
||||||
|
),
|
||||||
// Coconut
|
// Coconut
|
||||||
(
|
(
|
||||||
(BlockKind::Coconut, 0),
|
(BlockKind::Coconut, 0),
|
||||||
|
@ -150,10 +150,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
if win.get_mouse_down(minifb::MouseButton::Left) {
|
if win.get_mouse_down(minifb::MouseButton::Left) {
|
||||||
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Clamp) {
|
if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Clamp) {
|
||||||
let chunk_pos = (Vec2::<f64>::from(focus) + (Vec2::new(mx as f64, my as f64) * scale))
|
let chunk_pos = (Vec2::<f64>::from(focus)
|
||||||
|
+ (Vec2::new(mx as f64, my as f64) * scale))
|
||||||
.map(|e| e as i32);
|
.map(|e| e as i32);
|
||||||
let block_pos = chunk_pos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e * f as i32);
|
let block_pos = chunk_pos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e * f as i32);
|
||||||
println!("Block: ({}, {}), Chunk: ({}, {})", block_pos.x, block_pos.y, chunk_pos.x, chunk_pos.y);
|
println!(
|
||||||
|
"Block: ({}, {}), Chunk: ({}, {})",
|
||||||
|
block_pos.x, block_pos.y, chunk_pos.x, chunk_pos.y
|
||||||
|
);
|
||||||
if let Some(chunk) = sampler.get(chunk_pos) {
|
if let Some(chunk) = sampler.get(chunk_pos) {
|
||||||
//println!("Chunk info: {:#?}", chunk);
|
//println!("Chunk info: {:#?}", chunk);
|
||||||
if let Some(id) = &chunk.place {
|
if let Some(id) = &chunk.place {
|
||||||
|
@ -66,7 +66,11 @@ impl<'a> BlockGen<'a> {
|
|||||||
// Conservative range of radius: [8, 47]
|
// Conservative range of radius: [8, 47]
|
||||||
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
|
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
|
||||||
|
|
||||||
if cliff_sample.water_dist.map(|d| d > radius as f32).unwrap_or(true) {
|
if cliff_sample
|
||||||
|
.water_dist
|
||||||
|
.map(|d| d > radius as f32)
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
max_height.max(
|
max_height.max(
|
||||||
if cliff_pos.map(|e| e as f32).distance_squared(wpos)
|
if cliff_pos.map(|e| e as f32).distance_squared(wpos)
|
||||||
< (radius as f32 + tolerance).powf(2.0)
|
< (radius as f32 + tolerance).powf(2.0)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use rand::prelude::*;
|
|
||||||
use super::GenCtx;
|
use super::GenCtx;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
pub struct SellOrder {
|
pub struct SellOrder {
|
||||||
pub quantity: f32,
|
pub quantity: f32,
|
||||||
@ -47,20 +47,22 @@ pub fn buy_units<'a>(
|
|||||||
max_price: f32,
|
max_price: f32,
|
||||||
max_spend: f32,
|
max_spend: f32,
|
||||||
) -> (f32, f32) {
|
) -> (f32, f32) {
|
||||||
let mut sell_orders = sellers
|
let mut sell_orders = sellers.filter(|so| so.quantity > 0.0).collect::<Vec<_>>();
|
||||||
.filter(|so| so.quantity > 0.0)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
// Sort sell orders by price, cheapest first
|
// Sort sell orders by price, cheapest first
|
||||||
sell_orders.sort_by(|a, b| a.price.partial_cmp(&b.price).unwrap_or_else(|| panic!("{} and {}", a.price, b.price)));
|
sell_orders.sort_by(|a, b| {
|
||||||
|
a.price
|
||||||
|
.partial_cmp(&b.price)
|
||||||
|
.unwrap_or_else(|| panic!("{} and {}", a.price, b.price))
|
||||||
|
});
|
||||||
|
|
||||||
let mut quantity = 0.0;
|
let mut quantity = 0.0;
|
||||||
let mut spent = 0.0;
|
let mut spent = 0.0;
|
||||||
|
|
||||||
for order in sell_orders {
|
for order in sell_orders {
|
||||||
if
|
if quantity >= max_quantity || // We've purchased enough
|
||||||
quantity >= max_quantity || // We've purchased enough
|
|
||||||
spent >= max_spend || // We've spent enough
|
spent >= max_spend || // We've spent enough
|
||||||
order.price > max_price // Price is too high
|
order.price > max_price
|
||||||
|
// Price is too high
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,27 +1,23 @@
|
|||||||
mod econ;
|
mod econ;
|
||||||
|
|
||||||
use std::{
|
use crate::{
|
||||||
ops::Range,
|
sim::{SimChunk, WorldSim},
|
||||||
hash::Hash,
|
site::{Dungeon, Settlement, Site as WorldSite},
|
||||||
fmt,
|
util::{attempt, seed_expan},
|
||||||
};
|
};
|
||||||
use hashbrown::{HashMap, HashSet};
|
|
||||||
use vek::*;
|
|
||||||
use rand::prelude::*;
|
|
||||||
use rand_chacha::ChaChaRng;
|
|
||||||
use common::{
|
use common::{
|
||||||
|
astar::Astar,
|
||||||
|
path::Path,
|
||||||
|
spiral::Spiral2d,
|
||||||
|
store::{Id, Store},
|
||||||
terrain::TerrainChunkSize,
|
terrain::TerrainChunkSize,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
store::{Id, Store},
|
|
||||||
path::Path,
|
|
||||||
astar::Astar,
|
|
||||||
spiral::Spiral2d,
|
|
||||||
};
|
|
||||||
use crate::{
|
|
||||||
sim::{WorldSim, SimChunk},
|
|
||||||
site::{Site as WorldSite, Settlement, Dungeon},
|
|
||||||
util::{seed_expan, attempt},
|
|
||||||
};
|
};
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
use rand::prelude::*;
|
||||||
|
use rand_chacha::ChaChaRng;
|
||||||
|
use std::{fmt, hash::Hash, ops::Range};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
const CARDINALS: [Vec2<i32>; 4] = [
|
const CARDINALS: [Vec2<i32>; 4] = [
|
||||||
Vec2::new(1, 0),
|
Vec2::new(1, 0),
|
||||||
@ -107,13 +103,15 @@ impl Civs {
|
|||||||
// Temporary!
|
// Temporary!
|
||||||
for track in this.tracks.iter() {
|
for track in this.tracks.iter() {
|
||||||
for loc in track.path.iter() {
|
for loc in track.path.iter() {
|
||||||
ctx.sim.get_mut(*loc).unwrap().place = Some(this.civs.iter().next().unwrap().homeland);
|
ctx.sim.get_mut(*loc).unwrap().place =
|
||||||
|
Some(this.civs.iter().next().unwrap().homeland);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten ground around sites
|
// Flatten ground around sites
|
||||||
for site in this.sites.iter() {
|
for site in this.sites.iter() {
|
||||||
if let SiteKind::Settlement = &site.kind {} else {
|
if let SiteKind::Settlement = &site.kind {
|
||||||
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +122,16 @@ impl Civs {
|
|||||||
let flatten_radius = 10.0;
|
let flatten_radius = 10.0;
|
||||||
if let Some(center_alt) = ctx.sim.get_alt_approx(wpos) {
|
if let Some(center_alt) = ctx.sim.get_alt_approx(wpos) {
|
||||||
for offs in Spiral2d::new().take(radius.pow(2) as usize) {
|
for offs in Spiral2d::new().take(radius.pow(2) as usize) {
|
||||||
let center_alt = center_alt + if offs.magnitude_squared() <= 6i32.pow(2) { 16.0 } else { 0.0 }; // Raise the town centre up a little
|
let center_alt = center_alt
|
||||||
|
+ if offs.magnitude_squared() <= 6i32.pow(2) {
|
||||||
|
16.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}; // Raise the town centre up a little
|
||||||
let pos = site.center + offs;
|
let pos = site.center + offs;
|
||||||
let factor = (1.0 - (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) * 1.15;
|
let factor = (1.0
|
||||||
|
- (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius)
|
||||||
|
* 1.15;
|
||||||
ctx.sim
|
ctx.sim
|
||||||
.get_mut(pos)
|
.get_mut(pos)
|
||||||
// Don't disrupt chunks that are near water
|
// Don't disrupt chunks that are near water
|
||||||
@ -147,12 +152,20 @@ impl Civs {
|
|||||||
let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32);
|
let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32);
|
||||||
|
|
||||||
let world_site = match &site.kind {
|
let world_site = match &site.kind {
|
||||||
SiteKind::Settlement => WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), ctx.rng)),
|
SiteKind::Settlement => {
|
||||||
SiteKind::Dungeon => WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), ctx.rng)),
|
WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), ctx.rng))
|
||||||
|
},
|
||||||
|
SiteKind::Dungeon => {
|
||||||
|
WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), ctx.rng))
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let radius_chunks = (world_site.radius() / TerrainChunkSize::RECT_SIZE.x as f32).ceil() as usize;
|
let radius_chunks =
|
||||||
for pos in Spiral2d::new().map(|offs| site.center + offs).take((radius_chunks * 2).pow(2)) {
|
(world_site.radius() / TerrainChunkSize::RECT_SIZE.x as f32).ceil() as usize;
|
||||||
|
for pos in Spiral2d::new()
|
||||||
|
.map(|offs| site.center + offs)
|
||||||
|
.take((radius_chunks * 2).pow(2))
|
||||||
|
{
|
||||||
ctx.sim
|
ctx.sim
|
||||||
.get_mut(pos)
|
.get_mut(pos)
|
||||||
.map(|chunk| chunk.sites.push(world_site.clone()));
|
.map(|chunk| chunk.sites.push(world_site.clone()));
|
||||||
@ -167,9 +180,7 @@ impl Civs {
|
|||||||
|
|
||||||
pub fn place(&self, id: Id<Place>) -> &Place { self.places.get(id) }
|
pub fn place(&self, id: Id<Place>) -> &Place { self.places.get(id) }
|
||||||
|
|
||||||
pub fn sites(&self) -> impl Iterator<Item=&Site> + '_ {
|
pub fn sites(&self) -> impl Iterator<Item = &Site> + '_ { self.sites.iter() }
|
||||||
self.sites.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_info(&self) {
|
fn display_info(&self) {
|
||||||
for (id, civ) in self.civs.iter_ids() {
|
for (id, civ) in self.civs.iter_ids() {
|
||||||
@ -189,24 +200,39 @@ impl Civs {
|
|||||||
self.track_map
|
self.track_map
|
||||||
.get(&a)
|
.get(&a)
|
||||||
.and_then(|dests| dests.get(&b))
|
.and_then(|dests| dests.get(&b))
|
||||||
.or_else(|| self.track_map
|
.or_else(|| self.track_map.get(&b).and_then(|dests| dests.get(&a)))
|
||||||
.get(&b)
|
|
||||||
.and_then(|dests| dests.get(&a)))
|
|
||||||
.copied()
|
.copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an iterator over a site's neighbors
|
/// Return an iterator over a site's neighbors
|
||||||
fn neighbors(&self, site: Id<Site>) -> impl Iterator<Item = Id<Site>> + '_ {
|
fn neighbors(&self, site: Id<Site>) -> impl Iterator<Item = Id<Site>> + '_ {
|
||||||
let to = self.track_map.get(&site).map(|dests| dests.keys()).into_iter().flatten();
|
let to = self
|
||||||
let fro = self.track_map.iter().filter(move |(_, dests)| dests.contains_key(&site)).map(|(p, _)| p);
|
.track_map
|
||||||
|
.get(&site)
|
||||||
|
.map(|dests| dests.keys())
|
||||||
|
.into_iter()
|
||||||
|
.flatten();
|
||||||
|
let fro = self
|
||||||
|
.track_map
|
||||||
|
.iter()
|
||||||
|
.filter(move |(_, dests)| dests.contains_key(&site))
|
||||||
|
.map(|(p, _)| p);
|
||||||
to.chain(fro).filter(move |p| **p != site).copied()
|
to.chain(fro).filter(move |p| **p != site).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the cheapest route between two places
|
/// Find the cheapest route between two places
|
||||||
fn route_between(&self, a: Id<Site>, b: Id<Site>) -> Option<(Path<Id<Site>>, f32)> {
|
fn route_between(&self, a: Id<Site>, b: Id<Site>) -> Option<(Path<Id<Site>>, f32)> {
|
||||||
let heuristic = move |p: &Id<Site>| (self.sites.get(*p).center.distance_squared(self.sites.get(b).center) as f32).sqrt();
|
let heuristic = move |p: &Id<Site>| {
|
||||||
|
(self
|
||||||
|
.sites
|
||||||
|
.get(*p)
|
||||||
|
.center
|
||||||
|
.distance_squared(self.sites.get(b).center) as f32)
|
||||||
|
.sqrt()
|
||||||
|
};
|
||||||
let neighbors = |p: &Id<Site>| self.neighbors(*p);
|
let neighbors = |p: &Id<Site>| self.neighbors(*p);
|
||||||
let transition = |a: &Id<Site>, b: &Id<Site>| self.tracks.get(self.track_between(*a, *b).unwrap()).cost;
|
let transition =
|
||||||
|
|a: &Id<Site>, b: &Id<Site>| self.tracks.get(self.track_between(*a, *b).unwrap()).cost;
|
||||||
let satisfied = |p: &Id<Site>| *p == b;
|
let satisfied = |p: &Id<Site>| *p == b;
|
||||||
let mut astar = Astar::new(100, a, heuristic);
|
let mut astar = Astar::new(100, a, heuristic);
|
||||||
astar
|
astar
|
||||||
@ -248,7 +274,12 @@ impl Civs {
|
|||||||
Some(civ)
|
Some(civ)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn establish_place(&mut self, ctx: &mut GenCtx<impl Rng>, loc: Vec2<i32>, area: Range<usize>) -> Option<Id<Place>> {
|
fn establish_place(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut GenCtx<impl Rng>,
|
||||||
|
loc: Vec2<i32>,
|
||||||
|
area: Range<usize>,
|
||||||
|
) -> Option<Id<Place>> {
|
||||||
let mut dead = HashSet::new();
|
let mut dead = HashSet::new();
|
||||||
let mut alive = HashSet::new();
|
let mut alive = HashSet::new();
|
||||||
alive.insert(loc);
|
alive.insert(loc);
|
||||||
@ -258,7 +289,13 @@ impl Civs {
|
|||||||
for dir in CARDINALS.iter() {
|
for dir in CARDINALS.iter() {
|
||||||
if site_in_dir(&ctx.sim, cloc, *dir) {
|
if site_in_dir(&ctx.sim, cloc, *dir) {
|
||||||
let rloc = cloc + *dir;
|
let rloc = cloc + *dir;
|
||||||
if !dead.contains(&rloc) && ctx.sim.get(rloc).map(|c| c.place.is_none()).unwrap_or(false) {
|
if !dead.contains(&rloc)
|
||||||
|
&& ctx
|
||||||
|
.sim
|
||||||
|
.get(rloc)
|
||||||
|
.map(|c| c.place.is_none())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
alive.insert(rloc);
|
alive.insert(rloc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,7 +328,12 @@ impl Civs {
|
|||||||
Some(place)
|
Some(place)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn establish_site(&mut self, ctx: &mut GenCtx<impl Rng>, loc: Vec2<i32>, site_fn: impl FnOnce(Id<Place>) -> Site) -> Option<Id<Site>> {
|
fn establish_site(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut GenCtx<impl Rng>,
|
||||||
|
loc: Vec2<i32>,
|
||||||
|
site_fn: impl FnOnce(Id<Place>) -> Site,
|
||||||
|
) -> Option<Id<Site>> {
|
||||||
const SITE_AREA: Range<usize> = 64..256;
|
const SITE_AREA: Range<usize> = 64..256;
|
||||||
|
|
||||||
let place = match ctx.sim.get(loc).and_then(|site| site.place) {
|
let place = match ctx.sim.get(loc).and_then(|site| site.place) {
|
||||||
@ -303,7 +345,8 @@ impl Civs {
|
|||||||
|
|
||||||
// Find neighbors
|
// Find neighbors
|
||||||
const MAX_NEIGHBOR_DISTANCE: f32 = 250.0;
|
const MAX_NEIGHBOR_DISTANCE: f32 = 250.0;
|
||||||
let mut nearby = self.sites
|
let mut nearby = self
|
||||||
|
.sites
|
||||||
.iter_ids()
|
.iter_ids()
|
||||||
.map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt()))
|
.map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt()))
|
||||||
.filter(|(p, dist)| *dist < MAX_NEIGHBOR_DISTANCE)
|
.filter(|(p, dist)| *dist < MAX_NEIGHBOR_DISTANCE)
|
||||||
@ -320,10 +363,7 @@ impl Civs {
|
|||||||
.filter(|(_, route_cost)| *route_cost < cost * 3.0)
|
.filter(|(_, route_cost)| *route_cost < cost * 3.0)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
let track = self.tracks.insert(Track {
|
let track = self.tracks.insert(Track { cost, path });
|
||||||
cost,
|
|
||||||
path,
|
|
||||||
});
|
|
||||||
self.track_map
|
self.track_map
|
||||||
.entry(site)
|
.entry(site)
|
||||||
.or_default()
|
.or_default()
|
||||||
@ -342,15 +382,17 @@ impl Civs {
|
|||||||
|
|
||||||
// Trade stocks
|
// Trade stocks
|
||||||
// let mut stocks = TRADE_STOCKS;
|
// let mut stocks = TRADE_STOCKS;
|
||||||
// stocks.shuffle(ctx.rng); // Give each stock a chance to be traded first
|
// stocks.shuffle(ctx.rng); // Give each stock a chance to be traded
|
||||||
// for stock in stocks.iter().copied() {
|
// first for stock in stocks.iter().copied() {
|
||||||
// let mut sell_orders = self.sites
|
// let mut sell_orders = self.sites
|
||||||
// .iter_ids()
|
// .iter_ids()
|
||||||
// .map(|(id, site)| (id, {
|
// .map(|(id, site)| (id, {
|
||||||
// econ::SellOrder {
|
// econ::SellOrder {
|
||||||
// quantity: site.export_targets[stock].max(0.0).min(site.stocks[stock]),
|
// quantity:
|
||||||
// price: site.trade_states[stock].sell_belief.choose_price(ctx) * 1.25, // Trade cost
|
// site.export_targets[stock].max(0.0).min(site.stocks[stock]),
|
||||||
// q_sold: 0.0,
|
// price:
|
||||||
|
// site.trade_states[stock].sell_belief.choose_price(ctx) * 1.25, //
|
||||||
|
// Trade cost q_sold: 0.0,
|
||||||
// }
|
// }
|
||||||
// }))
|
// }))
|
||||||
// .filter(|(_, order)| order.quantity > 0.0)
|
// .filter(|(_, order)| order.quantity > 0.0)
|
||||||
@ -364,17 +406,17 @@ impl Civs {
|
|||||||
// let (max_spend, max_price, max_import) = {
|
// let (max_spend, max_price, max_import) = {
|
||||||
// let site = self.sites.get(site);
|
// let site = self.sites.get(site);
|
||||||
// let budget = site.coin * 0.5;
|
// let budget = site.coin * 0.5;
|
||||||
// let total_value = site.values.iter().map(|(_, v)| (*v).unwrap_or(0.0)).sum::<f32>();
|
// let total_value = site.values.iter().map(|(_, v)|
|
||||||
// (
|
// (*v).unwrap_or(0.0)).sum::<f32>(); (
|
||||||
// 100000.0,//(site.values[stock].unwrap_or(0.1) / total_value * budget).min(budget),
|
// 100000.0,//(site.values[stock].unwrap_or(0.1) /
|
||||||
|
// total_value * budget).min(budget),
|
||||||
// site.trade_states[stock].buy_belief.price,
|
// site.trade_states[stock].buy_belief.price,
|
||||||
// -site.export_targets[stock].min(0.0),
|
// -site.export_targets[stock].min(0.0), )
|
||||||
// )
|
|
||||||
// };
|
// };
|
||||||
// let (quantity, spent) = econ::buy_units(ctx, sell_orders
|
// let (quantity, spent) = econ::buy_units(ctx, sell_orders
|
||||||
// .iter_mut()
|
// .iter_mut()
|
||||||
// .filter(|(id, _)| site != *id && self.track_between(site, *id).is_some())
|
// .filter(|(id, _)| site != *id && self.track_between(site,
|
||||||
// .map(|(_, order)| order),
|
// *id).is_some()) .map(|(_, order)| order),
|
||||||
// max_import,
|
// max_import,
|
||||||
// 1000000.0, // Max price TODO
|
// 1000000.0, // Max price TODO
|
||||||
// max_spend,
|
// max_spend,
|
||||||
@ -384,9 +426,9 @@ impl Civs {
|
|||||||
// if quantity > 0.0 {
|
// if quantity > 0.0 {
|
||||||
// site.stocks[stock] += quantity;
|
// site.stocks[stock] += quantity;
|
||||||
// site.last_exports[stock] = -quantity;
|
// site.last_exports[stock] = -quantity;
|
||||||
// site.trade_states[stock].buy_belief.update_buyer(years, spent / quantity);
|
// site.trade_states[stock].buy_belief.update_buyer(years,
|
||||||
// println!("Belief: {:?}", site.trade_states[stock].buy_belief);
|
// spent / quantity); println!("Belief: {:?}",
|
||||||
// }
|
// site.trade_states[stock].buy_belief); }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// for (site, order) in sell_orders {
|
// for (site, order) in sell_orders {
|
||||||
@ -395,22 +437,31 @@ impl Civs {
|
|||||||
// if order.q_sold > 0.0 {
|
// if order.q_sold > 0.0 {
|
||||||
// site.stocks[stock] -= order.q_sold;
|
// site.stocks[stock] -= order.q_sold;
|
||||||
// site.last_exports[stock] = order.q_sold;
|
// site.last_exports[stock] = order.q_sold;
|
||||||
// site.trade_states[stock].sell_belief.update_seller(order.q_sold / order.quantity);
|
//
|
||||||
// }
|
// site.trade_states[stock].sell_belief.update_seller(order.q_sold /
|
||||||
|
// order.quantity); }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to find a path between two locations
|
/// Attempt to find a path between two locations
|
||||||
fn find_path(ctx: &mut GenCtx<impl Rng>, a: Vec2<i32>, b: Vec2<i32>) -> Option<(Path<Vec2<i32>>, f32)> {
|
fn find_path(
|
||||||
|
ctx: &mut GenCtx<impl Rng>,
|
||||||
|
a: Vec2<i32>,
|
||||||
|
b: Vec2<i32>,
|
||||||
|
) -> Option<(Path<Vec2<i32>>, f32)> {
|
||||||
let sim = &ctx.sim;
|
let sim = &ctx.sim;
|
||||||
let heuristic = move |l: &Vec2<i32>| (l.distance_squared(b) as f32).sqrt();
|
let heuristic = move |l: &Vec2<i32>| (l.distance_squared(b) as f32).sqrt();
|
||||||
let neighbors = |l: &Vec2<i32>| {
|
let neighbors = |l: &Vec2<i32>| {
|
||||||
let l = *l;
|
let l = *l;
|
||||||
DIAGONALS.iter().filter(move |dir| walk_in_dir(sim, l, **dir).is_some()).map(move |dir| l + *dir)
|
DIAGONALS
|
||||||
|
.iter()
|
||||||
|
.filter(move |dir| walk_in_dir(sim, l, **dir).is_some())
|
||||||
|
.map(move |dir| l + *dir)
|
||||||
};
|
};
|
||||||
let transition = |a: &Vec2<i32>, b: &Vec2<i32>| 1.0 + walk_in_dir(sim, *a, *b - *a).unwrap_or(10000.0);
|
let transition =
|
||||||
|
|a: &Vec2<i32>, b: &Vec2<i32>| 1.0 + walk_in_dir(sim, *a, *b - *a).unwrap_or(10000.0);
|
||||||
let satisfied = |l: &Vec2<i32>| *l == b;
|
let satisfied = |l: &Vec2<i32>| *l == b;
|
||||||
let mut astar = Astar::new(20000, a, heuristic);
|
let mut astar = Astar::new(20000, a, heuristic);
|
||||||
astar
|
astar
|
||||||
@ -419,11 +470,10 @@ fn find_path(ctx: &mut GenCtx<impl Rng>, a: Vec2<i32>, b: Vec2<i32>) -> Option<(
|
|||||||
.and_then(|path| astar.get_cheapest_cost().map(|cost| (path, cost)))
|
.and_then(|path| astar.get_cheapest_cost().map(|cost| (path, cost)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if travel between a location and a chunk next to it is permitted (TODO: by whom?)
|
/// Return true if travel between a location and a chunk next to it is permitted
|
||||||
|
/// (TODO: by whom?)
|
||||||
fn walk_in_dir(sim: &WorldSim, a: Vec2<i32>, dir: Vec2<i32>) -> Option<f32> {
|
fn walk_in_dir(sim: &WorldSim, a: Vec2<i32>, dir: Vec2<i32>) -> Option<f32> {
|
||||||
if loc_suitable_for_walking(sim, a) &&
|
if loc_suitable_for_walking(sim, a) && loc_suitable_for_walking(sim, a + dir) {
|
||||||
loc_suitable_for_walking(sim, a + dir)
|
|
||||||
{
|
|
||||||
let a_alt = sim.get(a)?.alt;
|
let a_alt = sim.get(a)?.alt;
|
||||||
let b_alt = sim.get(a + dir)?.alt;
|
let b_alt = sim.get(a + dir)?.alt;
|
||||||
Some((b_alt - a_alt).abs() / 2.5)
|
Some((b_alt - a_alt).abs() / 2.5)
|
||||||
@ -441,18 +491,22 @@ fn loc_suitable_for_walking(sim: &WorldSim, loc: Vec2<i32>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if a site could be constructed between a location and a chunk next to it is permitted (TODO: by whom?)
|
/// Return true if a site could be constructed between a location and a chunk
|
||||||
|
/// next to it is permitted (TODO: by whom?)
|
||||||
fn site_in_dir(sim: &WorldSim, a: Vec2<i32>, dir: Vec2<i32>) -> bool {
|
fn site_in_dir(sim: &WorldSim, a: Vec2<i32>, dir: Vec2<i32>) -> bool {
|
||||||
loc_suitable_for_site(sim, a) &&
|
loc_suitable_for_site(sim, a) && loc_suitable_for_site(sim, a + dir)
|
||||||
loc_suitable_for_site(sim, a + dir)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if a position is suitable for site construction (TODO: criteria?)
|
/// Return true if a position is suitable for site construction (TODO:
|
||||||
|
/// criteria?)
|
||||||
fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2<i32>) -> bool {
|
fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2<i32>) -> bool {
|
||||||
if let Some(chunk) = sim.get(loc) {
|
if let Some(chunk) = sim.get(loc) {
|
||||||
!chunk.river.is_ocean() &&
|
!chunk.river.is_ocean()
|
||||||
!chunk.river.is_lake() &&
|
&& !chunk.river.is_lake()
|
||||||
sim.get_gradient_approx(loc).map(|grad| grad < 1.0).unwrap_or(false)
|
&& sim
|
||||||
|
.get_gradient_approx(loc)
|
||||||
|
.map(|grad| grad < 1.0)
|
||||||
|
.unwrap_or(false)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@ -464,10 +518,15 @@ fn find_site_loc(ctx: &mut GenCtx<impl Rng>, near: Option<(Vec2<i32>, f32)>) ->
|
|||||||
let mut loc = None;
|
let mut loc = None;
|
||||||
for _ in 0..MAX_ATTEMPTS {
|
for _ in 0..MAX_ATTEMPTS {
|
||||||
let test_loc = loc.unwrap_or_else(|| match near {
|
let test_loc = loc.unwrap_or_else(|| match near {
|
||||||
Some((origin, dist)) => origin + (Vec2::new(
|
Some((origin, dist)) => {
|
||||||
ctx.rng.gen_range(-1.0, 1.0),
|
origin
|
||||||
ctx.rng.gen_range(-1.0, 1.0),
|
+ (Vec2::new(ctx.rng.gen_range(-1.0, 1.0), ctx.rng.gen_range(-1.0, 1.0))
|
||||||
).try_normalized().unwrap_or(Vec2::zero()) * ctx.rng.gen::<f32>() * dist).map(|e| e as i32),
|
.try_normalized()
|
||||||
|
.unwrap_or(Vec2::zero())
|
||||||
|
* ctx.rng.gen::<f32>()
|
||||||
|
* dist)
|
||||||
|
.map(|e| e as i32)
|
||||||
|
},
|
||||||
None => Vec2::new(
|
None => Vec2::new(
|
||||||
ctx.rng.gen_range(0, ctx.sim.get_size().x as i32),
|
ctx.rng.gen_range(0, ctx.sim.get_size().x as i32),
|
||||||
ctx.rng.gen_range(0, ctx.sim.get_size().y as i32),
|
ctx.rng.gen_range(0, ctx.sim.get_size().y as i32),
|
||||||
@ -478,9 +537,14 @@ fn find_site_loc(ctx: &mut GenCtx<impl Rng>, near: Option<(Vec2<i32>, f32)>) ->
|
|||||||
return Some(test_loc);
|
return Some(test_loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
loc = ctx.sim.get(test_loc).and_then(|c| Some(c.downhill?.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| {
|
loc = ctx.sim.get(test_loc).and_then(|c| {
|
||||||
|
Some(
|
||||||
|
c.downhill?
|
||||||
|
.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| {
|
||||||
e / (sz as i32)
|
e / (sz as i32)
|
||||||
})));
|
}),
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -508,23 +572,36 @@ pub struct NaturalResources {
|
|||||||
|
|
||||||
impl NaturalResources {
|
impl NaturalResources {
|
||||||
fn include_chunk(&mut self, ctx: &mut GenCtx<impl Rng>, loc: Vec2<i32>) {
|
fn include_chunk(&mut self, ctx: &mut GenCtx<impl Rng>, loc: Vec2<i32>) {
|
||||||
let chunk = if let Some(chunk) = ctx.sim.get(loc) { chunk } else { return };
|
let chunk = if let Some(chunk) = ctx.sim.get(loc) {
|
||||||
|
chunk
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
self.wood += chunk.tree_density;
|
self.wood += chunk.tree_density;
|
||||||
self.rock += chunk.rockiness;
|
self.rock += chunk.rockiness;
|
||||||
self.river += if chunk.river.is_river() { 5.0 } else { 0.0 };
|
self.river += if chunk.river.is_river() { 5.0 } else { 0.0 };
|
||||||
self.farmland += if
|
self.farmland += if chunk.humidity > 0.35
|
||||||
chunk.humidity > 0.35 &&
|
&& chunk.temp > -0.3
|
||||||
chunk.temp > -0.3 && chunk.temp < 0.75 &&
|
&& chunk.temp < 0.75
|
||||||
chunk.chaos < 0.5 &&
|
&& chunk.chaos < 0.5
|
||||||
ctx.sim.get_gradient_approx(loc).map(|grad| grad < 0.7).unwrap_or(false)
|
&& ctx
|
||||||
{ 1.0 } else { 0.0 };
|
.sim
|
||||||
|
.get_gradient_approx(loc)
|
||||||
|
.map(|grad| grad < 0.7)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Track {
|
pub struct Track {
|
||||||
/// Cost of using this track relative to other paths. This cost is an arbitrary unit and
|
/// Cost of using this track relative to other paths. This cost is an
|
||||||
/// doesn't make sense unless compared to other track costs.
|
/// arbitrary unit and doesn't make sense unless compared to other track
|
||||||
|
/// costs.
|
||||||
cost: f32,
|
cost: f32,
|
||||||
path: Path<Vec2<i32>>,
|
path: Path<Vec2<i32>>,
|
||||||
}
|
}
|
||||||
@ -570,7 +647,14 @@ impl fmt::Display for Site {
|
|||||||
}
|
}
|
||||||
writeln!(f, "Values")?;
|
writeln!(f, "Values")?;
|
||||||
for stock in TRADE_STOCKS.iter() {
|
for stock in TRADE_STOCKS.iter() {
|
||||||
writeln!(f, "- {}: {}", stock, self.values[*stock].map(|x| x.to_string()).unwrap_or_else(|| "N/A".to_string()))?;
|
writeln!(
|
||||||
|
f,
|
||||||
|
"- {}: {}",
|
||||||
|
stock,
|
||||||
|
self.values[*stock]
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.unwrap_or_else(|| "N/A".to_string())
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
writeln!(f, "Laborers")?;
|
writeln!(f, "Laborers")?;
|
||||||
for (labor, n) in self.labors.iter() {
|
for (labor, n) in self.labors.iter() {
|
||||||
@ -634,7 +718,11 @@ impl Site {
|
|||||||
|
|
||||||
let mut demand = Stocks::from_default(0.0);
|
let mut demand = Stocks::from_default(0.0);
|
||||||
for (labor, orders) in &orders {
|
for (labor, orders) in &orders {
|
||||||
let scale = if let Some(labor) = labor { self.labors[*labor] } else { 1.0 } * self.population;
|
let scale = if let Some(labor) = labor {
|
||||||
|
self.labors[*labor]
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
} * self.population;
|
||||||
for (stock, amount) in orders {
|
for (stock, amount) in orders {
|
||||||
demand[*stock] += *amount * scale;
|
demand[*stock] += *amount * scale;
|
||||||
}
|
}
|
||||||
@ -647,20 +735,27 @@ impl Site {
|
|||||||
|
|
||||||
let last_exports = &self.last_exports;
|
let last_exports = &self.last_exports;
|
||||||
let stocks = &self.stocks;
|
let stocks = &self.stocks;
|
||||||
self.surplus = demand.clone().map(|stock, tgt| {
|
self.surplus = demand
|
||||||
supply[stock] + stocks[stock] - demand[stock] - last_exports[stock]
|
.clone()
|
||||||
});
|
.map(|stock, tgt| supply[stock] + stocks[stock] - demand[stock] - last_exports[stock]);
|
||||||
|
|
||||||
// Update values according to the surplus of each stock
|
// Update values according to the surplus of each stock
|
||||||
let values = &mut self.values;
|
let values = &mut self.values;
|
||||||
self.surplus.iter().for_each(|(stock, surplus)| {
|
self.surplus.iter().for_each(|(stock, surplus)| {
|
||||||
let val = 3.5f32.powf(1.0 - *surplus / demand[stock]);
|
let val = 3.5f32.powf(1.0 - *surplus / demand[stock]);
|
||||||
values[stock] = if val > 0.001 && val < 1000.0 { Some(val) } else { None };
|
values[stock] = if val > 0.001 && val < 1000.0 {
|
||||||
|
Some(val)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update export targets based on relative values
|
// Update export targets based on relative values
|
||||||
let value_avg =
|
let value_avg = values
|
||||||
values.iter().map(|(_, v)| (*v).unwrap_or(0.0)).sum::<f32>().max(0.01)
|
.iter()
|
||||||
|
.map(|(_, v)| (*v).unwrap_or(0.0))
|
||||||
|
.sum::<f32>()
|
||||||
|
.max(0.01)
|
||||||
/ values.iter().filter(|(_, v)| v.is_some()).count() as f32;
|
/ values.iter().filter(|(_, v)| v.is_some()).count() as f32;
|
||||||
let export_targets = &mut self.export_targets;
|
let export_targets = &mut self.export_targets;
|
||||||
let last_exports = &self.last_exports;
|
let last_exports = &self.last_exports;
|
||||||
@ -680,17 +775,24 @@ impl Site {
|
|||||||
let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::<f32>().max(0.01);
|
let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::<f32>().max(0.01);
|
||||||
production.iter().for_each(|(labor, _)| {
|
production.iter().for_each(|(labor, _)| {
|
||||||
let smooth = 0.8;
|
let smooth = 0.8;
|
||||||
self.labors[labor] = smooth * self.labors[labor] + (1.0 - smooth) * (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum);
|
self.labors[labor] = smooth * self.labors[labor]
|
||||||
|
+ (1.0 - smooth)
|
||||||
|
* (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Production
|
// Production
|
||||||
let stocks_before = self.stocks.clone();
|
let stocks_before = self.stocks.clone();
|
||||||
for (labor, orders) in orders.iter() {
|
for (labor, orders) in orders.iter() {
|
||||||
let scale = if let Some(labor) = labor { self.labors[*labor] } else { 1.0 } * population;
|
let scale = if let Some(labor) = labor {
|
||||||
|
self.labors[*labor]
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
} * population;
|
||||||
|
|
||||||
// For each order, we try to find the minimum satisfaction rate - this limits how much
|
// For each order, we try to find the minimum satisfaction rate - this limits
|
||||||
// we can produce! For example, if we need 0.25 fish and 0.75 oats to make 1 unit of
|
// how much we can produce! For example, if we need 0.25 fish and
|
||||||
// food, but only 0.5 units of oats are available then we only need to consume 2/3rds
|
// 0.75 oats to make 1 unit of food, but only 0.5 units of oats are
|
||||||
|
// available then we only need to consume 2/3rds
|
||||||
// of other ingredients and leave the rest in stock
|
// of other ingredients and leave the rest in stock
|
||||||
// In effect, this is the productivity
|
// In effect, this is the productivity
|
||||||
let productivity = orders
|
let productivity = orders
|
||||||
@ -703,7 +805,9 @@ impl Site {
|
|||||||
satisfaction
|
satisfaction
|
||||||
})
|
})
|
||||||
.min_by(|a, b| a.partial_cmp(b).unwrap())
|
.min_by(|a, b| a.partial_cmp(b).unwrap())
|
||||||
.unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor));
|
.unwrap_or_else(|| {
|
||||||
|
panic!("Industry {:?} requires at least one input order", labor)
|
||||||
|
});
|
||||||
|
|
||||||
for (stock, amount) in orders {
|
for (stock, amount) in orders {
|
||||||
// What quantity is this order requesting?
|
// What quantity is this order requesting?
|
||||||
@ -733,7 +837,11 @@ impl Site {
|
|||||||
// Births/deaths
|
// Births/deaths
|
||||||
const NATURAL_BIRTH_RATE: f32 = 0.15;
|
const NATURAL_BIRTH_RATE: f32 = 0.15;
|
||||||
const DEATH_RATE: f32 = 0.05;
|
const DEATH_RATE: f32 = 0.05;
|
||||||
let birth_rate = if self.surplus[FOOD] > 0.0 { NATURAL_BIRTH_RATE } else { 0.0 };
|
let birth_rate = if self.surplus[FOOD] > 0.0 {
|
||||||
|
NATURAL_BIRTH_RATE
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
self.population += years * self.population * (birth_rate - DEATH_RATE);
|
self.population += years * self.population * (birth_rate - DEATH_RATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -757,13 +865,7 @@ const LOGS: Stock = "logs";
|
|||||||
const WOOD: Stock = "wood";
|
const WOOD: Stock = "wood";
|
||||||
const ROCK: Stock = "rock";
|
const ROCK: Stock = "rock";
|
||||||
const STONE: Stock = "stone";
|
const STONE: Stock = "stone";
|
||||||
const TRADE_STOCKS: [Stock; 5] = [
|
const TRADE_STOCKS: [Stock; 5] = [FLOUR, MEAT, FOOD, WOOD, STONE];
|
||||||
FLOUR,
|
|
||||||
MEAT,
|
|
||||||
FOOD,
|
|
||||||
WOOD,
|
|
||||||
STONE,
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TradeState {
|
struct TradeState {
|
||||||
@ -796,7 +898,9 @@ pub struct MapVec<K, T> {
|
|||||||
|
|
||||||
impl<K: Copy + Eq + Hash, T: Default + Clone> MapVec<K, T> {
|
impl<K: Copy + Eq + Hash, T: Default + Clone> MapVec<K, T> {
|
||||||
pub fn from_list<'a>(i: impl IntoIterator<Item = &'a (K, T)>) -> Self
|
pub fn from_list<'a>(i: impl IntoIterator<Item = &'a (K, T)>) -> Self
|
||||||
where K: 'a, T: 'a
|
where
|
||||||
|
K: 'a,
|
||||||
|
T: 'a,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
entries: i.into_iter().cloned().collect(),
|
entries: i.into_iter().cloned().collect(),
|
||||||
@ -813,19 +917,18 @@ impl<K: Copy + Eq + Hash, T: Default + Clone> MapVec<K, T> {
|
|||||||
|
|
||||||
pub fn get_mut(&mut self, entry: K) -> &mut T {
|
pub fn get_mut(&mut self, entry: K) -> &mut T {
|
||||||
let default = &self.default;
|
let default = &self.default;
|
||||||
self
|
self.entries.entry(entry).or_insert_with(|| default.clone())
|
||||||
.entries
|
|
||||||
.entry(entry)
|
|
||||||
.or_insert_with(|| default.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, entry: K) -> &T {
|
pub fn get(&self, entry: K) -> &T { self.entries.get(&entry).unwrap_or(&self.default) }
|
||||||
self.entries.get(&entry).unwrap_or(&self.default)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map<U: Default>(mut self, mut f: impl FnMut(K, T) -> U) -> MapVec<K, U> {
|
pub fn map<U: Default>(mut self, mut f: impl FnMut(K, T) -> U) -> MapVec<K, U> {
|
||||||
MapVec {
|
MapVec {
|
||||||
entries: self.entries.into_iter().map(|(s, v)| (s.clone(), f(s, v))).collect(),
|
entries: self
|
||||||
|
.entries
|
||||||
|
.into_iter()
|
||||||
|
.map(|(s, v)| (s.clone(), f(s, v)))
|
||||||
|
.collect(),
|
||||||
default: U::default(),
|
default: U::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -841,11 +944,10 @@ impl<K: Copy + Eq + Hash, T: Default + Clone> MapVec<K, T> {
|
|||||||
|
|
||||||
impl<K: Copy + Eq + Hash, T: Default + Clone> std::ops::Index<K> for MapVec<K, T> {
|
impl<K: Copy + Eq + Hash, T: Default + Clone> std::ops::Index<K> for MapVec<K, T> {
|
||||||
type Output = T;
|
type Output = T;
|
||||||
|
|
||||||
fn index(&self, entry: K) -> &Self::Output { self.get(entry) }
|
fn index(&self, entry: K) -> &Self::Output { self.get(entry) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Copy + Eq + Hash, T: Default + Clone> std::ops::IndexMut<K> for MapVec<K, T> {
|
impl<K: Copy + Eq + Hash, T: Default + Clone> std::ops::IndexMut<K> for MapVec<K, T> {
|
||||||
fn index_mut(&mut self, entry: K) -> &mut Self::Output { self.get_mut(entry) }
|
fn index_mut(&mut self, entry: K) -> &mut Self::Output { self.get_mut(entry) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
all::ForestKind,
|
all::ForestKind,
|
||||||
block::StructureMeta,
|
block::StructureMeta,
|
||||||
sim::{
|
sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim},
|
||||||
local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk,
|
|
||||||
WorldSim,
|
|
||||||
},
|
|
||||||
util::{RandomPerm, Sampler, UnitChooser},
|
util::{RandomPerm, Sampler, UnitChooser},
|
||||||
CONFIG,
|
CONFIG,
|
||||||
};
|
};
|
||||||
@ -598,21 +595,29 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
river_chunk,
|
river_chunk,
|
||||||
max_border_river,
|
max_border_river,
|
||||||
max_border_river_dist,
|
max_border_river_dist,
|
||||||
)) = max_river
|
)) =
|
||||||
|
max_river
|
||||||
{
|
{
|
||||||
// This is flowing into a lake, or a lake, or is at least a non-ocean tile.
|
// This is flowing into a lake, or a lake, or is at least a non-ocean tile.
|
||||||
//
|
//
|
||||||
// If we are <= water_alt, we are in the lake; otherwise, we are flowing into
|
// If we are <= water_alt, we are in the lake; otherwise, we are flowing into
|
||||||
// it.
|
// it.
|
||||||
let (in_water, water_dist, new_alt, new_water_alt, riverless_alt, warp_factor) = max_border_river
|
let (in_water, water_dist, new_alt, new_water_alt, riverless_alt, warp_factor) =
|
||||||
|
max_border_river
|
||||||
.river_kind
|
.river_kind
|
||||||
.and_then(|river_kind| {
|
.and_then(|river_kind| {
|
||||||
if let RiverKind::River { cross_section } = river_kind {
|
if let RiverKind::River { cross_section } = river_kind {
|
||||||
if max_border_river_dist.map(|(_, dist, _, _)| dist) != Some(Vec2::zero()) {
|
if max_border_river_dist.map(|(_, dist, _, _)| dist)
|
||||||
|
!= Some(Vec2::zero())
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (_, _, river_width, (river_t, (river_pos, _), downhill_river_chunk)) =
|
let (
|
||||||
max_border_river_dist.unwrap();
|
_,
|
||||||
|
_,
|
||||||
|
river_width,
|
||||||
|
(river_t, (river_pos, _), downhill_river_chunk),
|
||||||
|
) = max_border_river_dist.unwrap();
|
||||||
let river_alt = Lerp::lerp(
|
let river_alt = Lerp::lerp(
|
||||||
river_chunk.alt.max(river_chunk.water_alt),
|
river_chunk.alt.max(river_chunk.water_alt),
|
||||||
downhill_river_chunk.alt.max(downhill_river_chunk.water_alt),
|
downhill_river_chunk.alt.max(downhill_river_chunk.water_alt),
|
||||||
@ -661,7 +666,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
max_border_river_pos
|
max_border_river_pos
|
||||||
);
|
);
|
||||||
panic!(
|
panic!(
|
||||||
"Oceans should definitely have a downhill! ...Right?"
|
"Oceans should definitely have a downhill! \
|
||||||
|
...Right?"
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
let lake_water_alt = Lerp::lerp(
|
let lake_water_alt = Lerp::lerp(
|
||||||
@ -674,11 +680,13 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
|
|
||||||
if dist == Vec2::zero() {
|
if dist == Vec2::zero() {
|
||||||
let river_dist = wposf.distance(river_pos);
|
let river_dist = wposf.distance(river_pos);
|
||||||
let _river_height_factor = river_dist / (river_width * 0.5);
|
let _river_height_factor =
|
||||||
|
river_dist / (river_width * 0.5);
|
||||||
return Some((
|
return Some((
|
||||||
true,
|
true,
|
||||||
Some((river_dist - river_width * 0.5) as f32),
|
Some((river_dist - river_width * 0.5) as f32),
|
||||||
alt_for_river.min(lake_water_alt - 1.0 - river_gouge),
|
alt_for_river
|
||||||
|
.min(lake_water_alt - 1.0 - river_gouge),
|
||||||
lake_water_alt - river_gouge,
|
lake_water_alt - river_gouge,
|
||||||
alt_for_river.max(lake_water_alt),
|
alt_for_river.max(lake_water_alt),
|
||||||
0.0,
|
0.0,
|
||||||
@ -687,7 +695,10 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
|
|
||||||
Some((
|
Some((
|
||||||
river_scale_factor <= 1.0,
|
river_scale_factor <= 1.0,
|
||||||
Some((wposf.distance(river_pos) - river_width * 0.5) as f32),
|
Some(
|
||||||
|
(wposf.distance(river_pos) - river_width * 0.5)
|
||||||
|
as f32,
|
||||||
|
),
|
||||||
alt_for_river,
|
alt_for_river,
|
||||||
downhill_water_alt,
|
downhill_water_alt,
|
||||||
alt_for_river,
|
alt_for_river,
|
||||||
@ -704,8 +715,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
&& lake_id_dist.y >= -1
|
&& lake_id_dist.y >= -1
|
||||||
&& lake_id_dist.x <= 1
|
&& lake_id_dist.x <= 1
|
||||||
&& lake_id_dist.y <= 1;
|
&& lake_id_dist.y <= 1;
|
||||||
let in_bounds =
|
let in_bounds = in_bounds
|
||||||
in_bounds && (lake_id_dist.x >= 0 && lake_id_dist.y >= 0);
|
&& (lake_id_dist.x >= 0 && lake_id_dist.y >= 0);
|
||||||
let (_, dist, _, (river_t, _, downhill_river_chunk)) =
|
let (_, dist, _, (river_t, _, downhill_river_chunk)) =
|
||||||
if let Some(dist) = max_border_river_dist {
|
if let Some(dist) = max_border_river_dist {
|
||||||
dist
|
dist
|
||||||
@ -722,7 +733,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
> alt_for_river,
|
> alt_for_river,
|
||||||
Some(lake_dist as f32),
|
Some(lake_dist as f32),
|
||||||
alt_for_river,
|
alt_for_river,
|
||||||
(downhill_water_alt.max(river_chunk.water_alt)
|
(downhill_water_alt
|
||||||
|
.max(river_chunk.water_alt)
|
||||||
- river_gouge),
|
- river_gouge),
|
||||||
alt_for_river,
|
alt_for_river,
|
||||||
river_scale_factor as f32
|
river_scale_factor as f32
|
||||||
@ -752,7 +764,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
return Some((
|
return Some((
|
||||||
true,
|
true,
|
||||||
Some(lake_dist as f32),
|
Some(lake_dist as f32),
|
||||||
alt_for_river.min(lake_water_alt - 1.0 - river_gouge),
|
alt_for_river
|
||||||
|
.min(lake_water_alt - 1.0 - river_gouge),
|
||||||
lake_water_alt - river_gouge,
|
lake_water_alt - river_gouge,
|
||||||
alt_for_river.max(lake_water_alt),
|
alt_for_river.max(lake_water_alt),
|
||||||
0.0,
|
0.0,
|
||||||
@ -766,8 +779,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
let in_bounds_ =
|
let in_bounds_ = lake_dist
|
||||||
lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 0.5;
|
<= TerrainChunkSize::RECT_SIZE.x as f64 * 0.5;
|
||||||
if gouge_factor == 1.0 {
|
if gouge_factor == 1.0 {
|
||||||
return Some((
|
return Some((
|
||||||
true,
|
true,
|
||||||
@ -789,7 +802,8 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
downhill_water_alt
|
downhill_water_alt
|
||||||
} - river_gouge,
|
} - river_gouge,
|
||||||
alt_for_river,
|
alt_for_river,
|
||||||
river_scale_factor as f32 * (1.0 - gouge_factor),
|
river_scale_factor as f32
|
||||||
|
* (1.0 - gouge_factor),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -828,9 +842,23 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
river_scale_factor as f32,
|
river_scale_factor as f32,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
(in_water, water_dist, new_alt, new_water_alt, riverless_alt, warp_factor)
|
(
|
||||||
|
in_water,
|
||||||
|
water_dist,
|
||||||
|
new_alt,
|
||||||
|
new_water_alt,
|
||||||
|
riverless_alt,
|
||||||
|
warp_factor,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(false, None, alt_for_river, downhill_water_alt, alt_for_river, 1.0)
|
(
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
alt_for_river,
|
||||||
|
downhill_water_alt,
|
||||||
|
alt_for_river,
|
||||||
|
1.0,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let warp_factor = warp_factor * chunk_warp_factor;
|
let warp_factor = warp_factor * chunk_warp_factor;
|
||||||
// NOTE: To disable warp, uncomment this line.
|
// NOTE: To disable warp, uncomment this line.
|
||||||
@ -1106,8 +1134,13 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
|||||||
),
|
),
|
||||||
sub_surface_color,
|
sub_surface_color,
|
||||||
// No growing directly on bedrock.
|
// No growing directly on bedrock.
|
||||||
// And, no growing on sites that don't want them TODO: More precise than this when we apply trees as a post-processing layer
|
// And, no growing on sites that don't want them TODO: More precise than this when we
|
||||||
tree_density: if sim_chunk.sites.iter().all(|site| site.spawn_rules(wpos).trees) {
|
// apply trees as a post-processing layer
|
||||||
|
tree_density: if sim_chunk
|
||||||
|
.sites
|
||||||
|
.iter()
|
||||||
|
.all(|site| site.spawn_rules(wpos).trees)
|
||||||
|
{
|
||||||
Lerp::lerp(0.0, tree_density, alt.sub(2.0).sub(basement).mul(0.5))
|
Lerp::lerp(0.0, tree_density, alt.sub(2.0).sub(basement).mul(0.5))
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
mod all;
|
mod all;
|
||||||
mod block;
|
mod block;
|
||||||
|
pub mod civ;
|
||||||
mod column;
|
mod column;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod sim;
|
pub mod sim;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod civ;
|
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::config::CONFIG;
|
pub use crate::config::CONFIG;
|
||||||
@ -72,17 +72,20 @@ impl World {
|
|||||||
|
|
||||||
let chunk_wpos2d = Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
let chunk_wpos2d = Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
||||||
let grid_border = 4;
|
let grid_border = 4;
|
||||||
let zcache_grid =
|
let zcache_grid = Grid::populate_from(
|
||||||
Grid::populate_from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, |offs| {
|
TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2,
|
||||||
sampler.get_z_cache(chunk_wpos2d - grid_border + offs)
|
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs),
|
||||||
});
|
);
|
||||||
|
|
||||||
let air = Block::empty();
|
let air = Block::empty();
|
||||||
let stone = Block::new(BlockKind::Dense, zcache_grid
|
let stone = Block::new(
|
||||||
|
BlockKind::Dense,
|
||||||
|
zcache_grid
|
||||||
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
||||||
.and_then(|zcache| zcache.as_ref())
|
.and_then(|zcache| zcache.as_ref())
|
||||||
.map(|zcache| zcache.sample.stone_col)
|
.map(|zcache| zcache.sample.stone_col)
|
||||||
.unwrap_or(Rgb::new(125, 120, 130)));
|
.unwrap_or(Rgb::new(125, 120, 130)),
|
||||||
|
);
|
||||||
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
|
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
|
||||||
|
|
||||||
let _chunk_size2d = TerrainChunkSize::RECT_SIZE;
|
let _chunk_size2d = TerrainChunkSize::RECT_SIZE;
|
||||||
|
@ -218,9 +218,7 @@ impl RiverData {
|
|||||||
|
|
||||||
pub fn near_river(&self) -> bool { self.is_river() || self.neighbor_rivers.len() > 0 }
|
pub fn near_river(&self) -> bool { self.is_river() || self.neighbor_rivers.len() > 0 }
|
||||||
|
|
||||||
pub fn near_water(&self) -> bool {
|
pub fn near_water(&self) -> bool { self.near_river() || self.is_lake() || self.is_ocean() }
|
||||||
self.near_river() || self.is_lake() || self.is_ocean()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw rivers and assign them heights, widths, and velocities. Take some
|
/// Draw rivers and assign them heights, widths, and velocities. Take some
|
||||||
|
@ -24,17 +24,17 @@ pub use self::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
all::ForestKind,
|
all::ForestKind,
|
||||||
block::BlockGen,
|
block::BlockGen,
|
||||||
|
civ::Place,
|
||||||
column::ColumnGen,
|
column::ColumnGen,
|
||||||
site::{Settlement, Site},
|
site::{Settlement, Site},
|
||||||
civ::Place,
|
|
||||||
util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d},
|
util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d},
|
||||||
CONFIG,
|
CONFIG,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
assets,
|
assets,
|
||||||
|
store::Id,
|
||||||
terrain::{BiomeKind, TerrainChunkSize},
|
terrain::{BiomeKind, TerrainChunkSize},
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
store::Id,
|
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use noise::{
|
use noise::{
|
||||||
@ -1305,9 +1305,7 @@ impl WorldSim {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size(&self) -> Vec2<u32> {
|
pub fn get_size(&self) -> Vec2<u32> { WORLD_SIZE.map(|e| e as u32) }
|
||||||
WORLD_SIZE.map(|e| e as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw a map of the world based on chunk information. Returns a buffer of
|
/// Draw a map of the world based on chunk information. Returns a buffer of
|
||||||
/// u32s.
|
/// u32s.
|
||||||
@ -1558,9 +1556,11 @@ impl WorldSim {
|
|||||||
pub fn get_gradient_approx(&self, chunk_pos: Vec2<i32>) -> Option<f32> {
|
pub fn get_gradient_approx(&self, chunk_pos: Vec2<i32>) -> Option<f32> {
|
||||||
let a = self.get(chunk_pos)?;
|
let a = self.get(chunk_pos)?;
|
||||||
if let Some(downhill) = a.downhill {
|
if let Some(downhill) = a.downhill {
|
||||||
let b = self.get(downhill.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| {
|
let b = self.get(
|
||||||
|
downhill.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| {
|
||||||
e / (sz as i32)
|
e / (sz as i32)
|
||||||
}))?;
|
}),
|
||||||
|
)?;
|
||||||
Some((a.alt - b.alt).abs() / TerrainChunkSize::RECT_SIZE.x as f32)
|
Some((a.alt - b.alt).abs() / TerrainChunkSize::RECT_SIZE.x as f32)
|
||||||
} else {
|
} else {
|
||||||
Some(0.0)
|
Some(0.0)
|
||||||
@ -1868,7 +1868,8 @@ impl SimChunk {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
//let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + chaos * 0.2;
|
//let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 +
|
||||||
|
// chaos * 0.2;
|
||||||
let cliff = 0.0; // Disable cliffs
|
let cliff = 0.0; // Disable cliffs
|
||||||
|
|
||||||
// Logistic regression. Make sure x ∈ (0, 1).
|
// Logistic regression. Make sure x ∈ (0, 1).
|
||||||
@ -2033,7 +2034,9 @@ impl SimChunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_underwater(&self) -> bool { self.water_alt > self.alt || self.river.river_kind.is_some() }
|
pub fn is_underwater(&self) -> bool {
|
||||||
|
self.water_alt > self.alt || self.river.river_kind.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_base_z(&self) -> f32 { self.alt - self.chaos * 50.0 - 16.0 }
|
pub fn get_base_z(&self) -> f32 { self.alt - self.chaos * 50.0 - 16.0 }
|
||||||
|
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
|
use super::SpawnRules;
|
||||||
use crate::{
|
use crate::{
|
||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
sim::{SimChunk, WorldSim},
|
sim::{SimChunk, WorldSim},
|
||||||
util::{attempt, Grid, RandomField, Sampler, StructureGen2d},
|
|
||||||
site::BlockMask,
|
site::BlockMask,
|
||||||
|
util::{attempt, Grid, RandomField, Sampler, StructureGen2d},
|
||||||
};
|
};
|
||||||
use super::SpawnRules;
|
|
||||||
use common::{
|
use common::{
|
||||||
astar::Astar,
|
astar::Astar,
|
||||||
|
comp::Alignment,
|
||||||
|
generation::{ChunkSupplement, EntityInfo, EntityKind},
|
||||||
path::Path,
|
path::Path,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
terrain::{Block, BlockKind, TerrainChunkSize},
|
|
||||||
vol::{BaseVol, RectSizedVol, RectVolSize, ReadVol, WriteVol, Vox},
|
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
generation::{ChunkSupplement, EntityInfo},
|
terrain::{Block, BlockKind, TerrainChunkSize},
|
||||||
comp::{Alignment},
|
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||||
};
|
};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -22,11 +22,8 @@ use vek::*;
|
|||||||
|
|
||||||
impl WorldSim {
|
impl WorldSim {
|
||||||
fn can_host_dungeon(&self, pos: Vec2<i32>) -> bool {
|
fn can_host_dungeon(&self, pos: Vec2<i32>) -> bool {
|
||||||
self
|
self.get(pos)
|
||||||
.get(pos)
|
.map(|chunk| !chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake())
|
||||||
.map(|chunk| {
|
|
||||||
!chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake()
|
|
||||||
})
|
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
&& self
|
&& self
|
||||||
.get_gradient_approx(pos)
|
.get_gradient_approx(pos)
|
||||||
@ -52,7 +49,11 @@ impl Dungeon {
|
|||||||
let mut ctx = GenCtx { sim, rng };
|
let mut ctx = GenCtx { sim, rng };
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
origin: wpos,
|
origin: wpos,
|
||||||
alt: ctx.sim.and_then(|sim| sim.get_alt_approx(wpos)).unwrap_or(0.0) as i32 + 6,
|
alt: ctx
|
||||||
|
.sim
|
||||||
|
.and_then(|sim| sim.get_alt_approx(wpos))
|
||||||
|
.unwrap_or(0.0) as i32
|
||||||
|
+ 6,
|
||||||
noise: RandomField::new(ctx.rng.gen()),
|
noise: RandomField::new(ctx.rng.gen()),
|
||||||
floors: (0..6)
|
floors: (0..6)
|
||||||
.scan(Vec2::zero(), |stair_tile, level| {
|
.scan(Vec2::zero(), |stair_tile, level| {
|
||||||
@ -196,8 +197,14 @@ pub struct Floor {
|
|||||||
const FLOOR_SIZE: Vec2<i32> = Vec2::new(18, 18);
|
const FLOOR_SIZE: Vec2<i32> = Vec2::new(18, 18);
|
||||||
|
|
||||||
impl Floor {
|
impl Floor {
|
||||||
pub fn generate(ctx: &mut GenCtx<impl Rng>, stair_tile: Vec2<i32>, level: i32) -> (Self, Vec2<i32>) {
|
pub fn generate(
|
||||||
let new_stair_tile = std::iter::from_fn(|| Some(FLOOR_SIZE.map(|sz| ctx.rng.gen_range(-sz / 2 + 1, sz / 2))))
|
ctx: &mut GenCtx<impl Rng>,
|
||||||
|
stair_tile: Vec2<i32>,
|
||||||
|
level: i32,
|
||||||
|
) -> (Self, Vec2<i32>) {
|
||||||
|
let new_stair_tile = std::iter::from_fn(|| {
|
||||||
|
Some(FLOOR_SIZE.map(|sz| ctx.rng.gen_range(-sz / 2 + 1, sz / 2)))
|
||||||
|
})
|
||||||
.filter(|pos| *pos != stair_tile)
|
.filter(|pos| *pos != stair_tile)
|
||||||
.take(8)
|
.take(8)
|
||||||
.max_by_key(|pos| (*pos - stair_tile).map(|e| e.abs()).sum())
|
.max_by_key(|pos| (*pos - stair_tile).map(|e| e.abs()).sum())
|
||||||
@ -208,11 +215,7 @@ impl Floor {
|
|||||||
tile_offset,
|
tile_offset,
|
||||||
tiles: Grid::new(FLOOR_SIZE, Tile::Solid),
|
tiles: Grid::new(FLOOR_SIZE, Tile::Solid),
|
||||||
rooms: Store::default(),
|
rooms: Store::default(),
|
||||||
solid_depth: if level == 0 {
|
solid_depth: if level == 0 { 80 } else { 13 * 2 },
|
||||||
80
|
|
||||||
} else {
|
|
||||||
13 * 2
|
|
||||||
},
|
|
||||||
hollow_depth: 13,
|
hollow_depth: 13,
|
||||||
stair_tile: new_stair_tile - tile_offset,
|
stair_tile: new_stair_tile - tile_offset,
|
||||||
};
|
};
|
||||||
@ -224,10 +227,16 @@ impl Floor {
|
|||||||
this.create_route(ctx, a.center(), b.center(), true);
|
this.create_route(ctx, a.center(), b.center(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.create_route(ctx, stair_tile - tile_offset, new_stair_tile - tile_offset, false);
|
this.create_route(
|
||||||
|
ctx,
|
||||||
|
stair_tile - tile_offset,
|
||||||
|
new_stair_tile - tile_offset,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
this.tiles.set(stair_tile - tile_offset, Tile::UpStair);
|
this.tiles.set(stair_tile - tile_offset, Tile::UpStair);
|
||||||
this.tiles.set(new_stair_tile - tile_offset, Tile::DownStair);
|
this.tiles
|
||||||
|
.set(new_stair_tile - tile_offset, Tile::DownStair);
|
||||||
|
|
||||||
(this, new_stair_tile)
|
(this, new_stair_tile)
|
||||||
}
|
}
|
||||||
@ -238,15 +247,16 @@ impl Floor {
|
|||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
let area = match attempt(30, || {
|
let area = match attempt(30, || {
|
||||||
let sz = Vec2::<i32>::zero().map(|_| ctx.rng.gen_range(dim_limits.0, dim_limits.1));
|
let sz = Vec2::<i32>::zero().map(|_| ctx.rng.gen_range(dim_limits.0, dim_limits.1));
|
||||||
let pos = FLOOR_SIZE.map2(sz, |floor_sz, room_sz| ctx.rng.gen_range(0, floor_sz + 1 - room_sz));
|
let pos = FLOOR_SIZE.map2(sz, |floor_sz, room_sz| {
|
||||||
|
ctx.rng.gen_range(0, floor_sz + 1 - room_sz)
|
||||||
|
});
|
||||||
let area = Rect::from((pos, Extent2::from(sz)));
|
let area = Rect::from((pos, Extent2::from(sz)));
|
||||||
let area_border = Rect::from((pos - 1, Extent2::from(sz) + 2)); // The room, but with some personal space
|
let area_border = Rect::from((pos - 1, Extent2::from(sz) + 2)); // The room, but with some personal space
|
||||||
|
|
||||||
// Ensure no overlap
|
// Ensure no overlap
|
||||||
if self.rooms
|
if self.rooms.iter().any(|r| {
|
||||||
.iter()
|
r.area.collides_with_rect(area_border) || r.area.contains_point(self.stair_tile)
|
||||||
.any(|r| r.area.collides_with_rect(area_border) || r.area.contains_point(self.stair_tile))
|
}) {
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,18 +275,27 @@ impl Floor {
|
|||||||
|
|
||||||
for x in 0..area.extent().w {
|
for x in 0..area.extent().w {
|
||||||
for y in 0..area.extent().h {
|
for y in 0..area.extent().h {
|
||||||
self.tiles.set(area.position() + Vec2::new(x, y), Tile::Room(room));
|
self.tiles
|
||||||
|
.set(area.position() + Vec2::new(x, y), Tile::Room(room));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_route(&mut self, ctx: &mut GenCtx<impl Rng>, a: Vec2<i32>, b: Vec2<i32>, optimise_longest: bool) {
|
fn create_route(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut GenCtx<impl Rng>,
|
||||||
|
a: Vec2<i32>,
|
||||||
|
b: Vec2<i32>,
|
||||||
|
optimise_longest: bool,
|
||||||
|
) {
|
||||||
let sim = &ctx.sim;
|
let sim = &ctx.sim;
|
||||||
let heuristic = move |l: &Vec2<i32>| if optimise_longest {
|
let heuristic = move |l: &Vec2<i32>| {
|
||||||
|
if optimise_longest {
|
||||||
(l.distance_squared(b) as f32).sqrt()
|
(l.distance_squared(b) as f32).sqrt()
|
||||||
} else {
|
} else {
|
||||||
100.0 - (l.distance_squared(b) as f32).sqrt()
|
100.0 - (l.distance_squared(b) as f32).sqrt()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let neighbors = |l: &Vec2<i32>| {
|
let neighbors = |l: &Vec2<i32>| {
|
||||||
let l = *l;
|
let l = *l;
|
||||||
@ -294,7 +313,13 @@ impl Floor {
|
|||||||
let satisfied = |l: &Vec2<i32>| *l == b;
|
let satisfied = |l: &Vec2<i32>| *l == b;
|
||||||
let mut astar = Astar::new(20000, a, heuristic);
|
let mut astar = Astar::new(20000, a, heuristic);
|
||||||
let path = astar
|
let path = astar
|
||||||
.poll(FLOOR_SIZE.product() as usize + 1, heuristic, neighbors, transition, satisfied)
|
.poll(
|
||||||
|
FLOOR_SIZE.product() as usize + 1,
|
||||||
|
heuristic,
|
||||||
|
neighbors,
|
||||||
|
transition,
|
||||||
|
satisfied,
|
||||||
|
)
|
||||||
.into_path()
|
.into_path()
|
||||||
.expect("No route between locations - this shouldn't be able to happen");
|
.expect("No route between locations - this shouldn't be able to happen");
|
||||||
|
|
||||||
@ -305,11 +330,19 @@ impl Floor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_supplement(&self, area: Aabr<i32>, origin: Vec3<i32>, supplement: &mut ChunkSupplement) {
|
pub fn apply_supplement(
|
||||||
let align = |e: i32| e.div_euclid(TILE_SIZE) + if e.rem_euclid(TILE_SIZE) > TILE_SIZE / 2 {
|
&self,
|
||||||
|
area: Aabr<i32>,
|
||||||
|
origin: Vec3<i32>,
|
||||||
|
supplement: &mut ChunkSupplement,
|
||||||
|
) {
|
||||||
|
let align = |e: i32| {
|
||||||
|
e.div_euclid(TILE_SIZE)
|
||||||
|
+ if e.rem_euclid(TILE_SIZE) > TILE_SIZE / 2 {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let aligned_area = Aabr {
|
let aligned_area = Aabr {
|
||||||
min: area.min.map(align) + self.tile_offset,
|
min: area.min.map(align) + self.tile_offset,
|
||||||
@ -321,8 +354,14 @@ impl Floor {
|
|||||||
let tile_pos = Vec2::new(x, y);
|
let tile_pos = Vec2::new(x, y);
|
||||||
if let Some(Tile::Room(room)) = self.tiles.get(tile_pos) {
|
if let Some(Tile::Room(room)) = self.tiles.get(tile_pos) {
|
||||||
let room = &self.rooms[*room];
|
let room = &self.rooms[*room];
|
||||||
if room.enemies && tile_pos.x % 4 == 0 && tile_pos.y % 4 == 0 { // Bad
|
if room.enemies && tile_pos.x % 4 == 0 && tile_pos.y % 4 == 0 {
|
||||||
let entity = EntityInfo::at((origin + Vec3::from(self.tile_offset + tile_pos) * TILE_SIZE + TILE_SIZE / 2).map(|e| e as f32))
|
// Bad
|
||||||
|
let entity = EntityInfo::at(
|
||||||
|
(origin
|
||||||
|
+ Vec3::from(self.tile_offset + tile_pos) * TILE_SIZE
|
||||||
|
+ TILE_SIZE / 2)
|
||||||
|
.map(|e| e as f32),
|
||||||
|
)
|
||||||
.into_giant()
|
.into_giant()
|
||||||
.with_alignment(Alignment::Enemy);
|
.with_alignment(Alignment::Enemy);
|
||||||
supplement.add_entity(entity);
|
supplement.add_entity(entity);
|
||||||
@ -332,22 +371,26 @@ impl Floor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_depth(&self) -> i32 {
|
pub fn total_depth(&self) -> i32 { self.solid_depth + self.hollow_depth }
|
||||||
self.solid_depth + self.hollow_depth
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nearest_wall(&self, rpos: Vec2<i32>) -> Option<Vec2<i32>> {
|
pub fn nearest_wall(&self, rpos: Vec2<i32>) -> Option<Vec2<i32>> {
|
||||||
let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE));
|
let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE));
|
||||||
let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2;
|
let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2;
|
||||||
|
|
||||||
DIRS
|
DIRS.iter()
|
||||||
.iter()
|
|
||||||
.map(|dir| tile_pos + *dir)
|
.map(|dir| tile_pos + *dir)
|
||||||
.filter(|other_tile_pos| self.tiles.get(*other_tile_pos).filter(|tile| tile.is_passable()).is_none())
|
.filter(|other_tile_pos| {
|
||||||
.map(|other_tile_pos| rpos.clamped(
|
self.tiles
|
||||||
|
.get(*other_tile_pos)
|
||||||
|
.filter(|tile| tile.is_passable())
|
||||||
|
.is_none()
|
||||||
|
})
|
||||||
|
.map(|other_tile_pos| {
|
||||||
|
rpos.clamped(
|
||||||
other_tile_pos * TILE_SIZE,
|
other_tile_pos * TILE_SIZE,
|
||||||
(other_tile_pos + 1) * TILE_SIZE - 1,
|
(other_tile_pos + 1) * TILE_SIZE - 1,
|
||||||
))
|
)
|
||||||
|
})
|
||||||
.min_by_key(|nearest| rpos.distance_squared(*nearest))
|
.min_by_key(|nearest| rpos.distance_squared(*nearest))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +408,11 @@ impl Floor {
|
|||||||
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) {
|
||||||
stone
|
stone
|
||||||
} else if (pos.xy().magnitude_squared() as f32) < radius.powf(2.0) {
|
} else if (pos.xy().magnitude_squared() as f32) < radius.powf(2.0) {
|
||||||
if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z as f32).rem_euclid(stretch) < 1.5 {
|
if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch
|
||||||
|
+ pos.z as f32)
|
||||||
|
.rem_euclid(stretch)
|
||||||
|
< 1.5
|
||||||
|
{
|
||||||
stone
|
stone
|
||||||
} else {
|
} else {
|
||||||
empty
|
empty
|
||||||
@ -376,11 +423,14 @@ impl Floor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let wall_thickness = 3.0;
|
let wall_thickness = 3.0;
|
||||||
let dist_to_wall = self.nearest_wall(rpos).map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()).unwrap_or(TILE_SIZE as f32);
|
let dist_to_wall = self
|
||||||
let tunnel_dist = 1.0 - (dist_to_wall.powf(2.0) - wall_thickness).max(0.0).sqrt() / TILE_SIZE as f32;
|
.nearest_wall(rpos)
|
||||||
|
.map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt())
|
||||||
|
.unwrap_or(TILE_SIZE as f32);
|
||||||
|
let tunnel_dist =
|
||||||
|
1.0 - (dist_to_wall.powf(2.0) - wall_thickness).max(0.0).sqrt() / TILE_SIZE as f32;
|
||||||
|
|
||||||
move |z| {
|
move |z| match self.tiles.get(tile_pos) {
|
||||||
match self.tiles.get(tile_pos) {
|
|
||||||
Some(Tile::Solid) => BlockMask::nothing(),
|
Some(Tile::Solid) => BlockMask::nothing(),
|
||||||
Some(Tile::Tunnel) => {
|
Some(Tile::Tunnel) => {
|
||||||
if (z as f32) < 8.0 - 8.0 * tunnel_dist.powf(4.0) {
|
if (z as f32) < 8.0 - 8.0 * tunnel_dist.powf(4.0) {
|
||||||
@ -389,18 +439,32 @@ impl Floor {
|
|||||||
BlockMask::nothing()
|
BlockMask::nothing()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(Tile::Room(_)) | Some(Tile::DownStair) if dist_to_wall < wall_thickness || z as f32 >= self.hollow_depth as f32 - 13.0 * tunnel_dist.powf(4.0) => BlockMask::nothing(),
|
Some(Tile::Room(_)) | Some(Tile::DownStair)
|
||||||
|
if dist_to_wall < wall_thickness
|
||||||
|
|| z as f32 >= self.hollow_depth as f32 - 13.0 * tunnel_dist.powf(4.0) =>
|
||||||
|
{
|
||||||
|
BlockMask::nothing()
|
||||||
|
},
|
||||||
Some(Tile::Room(room)) => {
|
Some(Tile::Room(room)) => {
|
||||||
let room = &self.rooms[*room];
|
let room = &self.rooms[*room];
|
||||||
if z == 0 && RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density) {
|
if z == 0 && RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density)
|
||||||
|
{
|
||||||
BlockMask::new(Block::new(BlockKind::Chest, Rgb::white()), 1)
|
BlockMask::new(Block::new(BlockKind::Chest, Rgb::white()), 1)
|
||||||
} else {
|
} else {
|
||||||
empty
|
empty
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(Tile::DownStair) => make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), 0.0, 0.5, 9.0).resolve_with(empty),
|
Some(Tile::DownStair) => {
|
||||||
|
make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), 0.0, 0.5, 9.0)
|
||||||
|
.resolve_with(empty)
|
||||||
|
},
|
||||||
Some(Tile::UpStair) => {
|
Some(Tile::UpStair) => {
|
||||||
let mut block = make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), TILE_SIZE as f32 / 2.0, 0.5, 9.0);
|
let mut block = make_staircase(
|
||||||
|
Vec3::new(rtile_pos.x, rtile_pos.y, z),
|
||||||
|
TILE_SIZE as f32 / 2.0,
|
||||||
|
0.5,
|
||||||
|
9.0,
|
||||||
|
);
|
||||||
if z < self.hollow_depth {
|
if z < self.hollow_depth {
|
||||||
block = block.resolve_with(empty);
|
block = block.resolve_with(empty);
|
||||||
}
|
}
|
||||||
@ -410,4 +474,3 @@ impl Floor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
mod settlement;
|
|
||||||
mod dungeon;
|
mod dungeon;
|
||||||
|
mod settlement;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use self::settlement::Settlement;
|
pub use self::{dungeon::Dungeon, settlement::Settlement};
|
||||||
pub use self::dungeon::Dungeon;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
util::{Grid, Sampler},
|
util::{Grid, Sampler},
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::Block,
|
|
||||||
vol::{Vox, BaseVol, RectSizedVol, ReadVol, WriteVol},
|
|
||||||
generation::ChunkSupplement,
|
generation::ChunkSupplement,
|
||||||
|
terrain::Block,
|
||||||
|
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
|
||||||
};
|
};
|
||||||
use std::{fmt, sync::Arc};
|
use std::{fmt, sync::Arc};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -24,9 +23,7 @@ pub struct BlockMask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlockMask {
|
impl BlockMask {
|
||||||
pub fn new(block: Block, priority: i32) -> Self {
|
pub fn new(block: Block, priority: i32) -> Self { Self { block, priority } }
|
||||||
Self { block, priority }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn nothing() -> Self {
|
pub fn nothing() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -62,11 +59,7 @@ pub struct SpawnRules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SpawnRules {
|
impl Default for SpawnRules {
|
||||||
fn default() -> Self {
|
fn default() -> Self { Self { trees: true } }
|
||||||
Self {
|
|
||||||
trees: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -109,7 +102,9 @@ impl Site {
|
|||||||
supplement: &mut ChunkSupplement,
|
supplement: &mut ChunkSupplement,
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
Site::Settlement(settlement) => settlement.apply_supplement(wpos2d, get_column, supplement),
|
Site::Settlement(settlement) => {
|
||||||
|
settlement.apply_supplement(wpos2d, get_column, supplement)
|
||||||
|
},
|
||||||
Site::Dungeon(dungeon) => dungeon.apply_supplement(wpos2d, get_column, supplement),
|
Site::Dungeon(dungeon) => dungeon.apply_supplement(wpos2d, get_column, supplement),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
use vek::*;
|
use super::{super::skeleton::*, Archetype};
|
||||||
use rand::prelude::*;
|
use crate::{
|
||||||
|
site::BlockMask,
|
||||||
|
util::{RandomField, Sampler},
|
||||||
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
vol::Vox,
|
vol::Vox,
|
||||||
};
|
};
|
||||||
use crate::{
|
use rand::prelude::*;
|
||||||
util::{RandomField, Sampler},
|
use vek::*;
|
||||||
site::BlockMask,
|
|
||||||
};
|
|
||||||
use super::{
|
|
||||||
Archetype,
|
|
||||||
super::skeleton::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
const COLOR_THEMES: [Rgb<u8>; 11] = [
|
const COLOR_THEMES: [Rgb<u8>; 11] = [
|
||||||
Rgb::new(0x1D, 0x4D, 0x45),
|
Rgb::new(0x1D, 0x4D, 0x45),
|
||||||
@ -53,8 +50,21 @@ enum StoreyFill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StoreyFill {
|
impl StoreyFill {
|
||||||
fn has_lower(&self) -> bool { if let StoreyFill::All = self { true } else { false } }
|
fn has_lower(&self) -> bool {
|
||||||
fn has_upper(&self) -> bool { if let StoreyFill::None = self { false } else { true } }
|
if let StoreyFill::All = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_upper(&self) -> bool {
|
||||||
|
if let StoreyFill::None = self {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Attr {
|
pub struct Attr {
|
||||||
@ -116,16 +126,21 @@ impl Archetype for House {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|flip| (0..branches_per_side).map(move |i| (i, *flip)))
|
.map(|flip| (0..branches_per_side).map(move |i| (i, *flip)))
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter_map(|(i, flip)| if rng.gen() {
|
.filter_map(|(i, flip)| {
|
||||||
Some((i as i32 * len / (branches_per_side - 1).max(1) as i32, Branch {
|
if rng.gen() {
|
||||||
|
Some((
|
||||||
|
i as i32 * len / (branches_per_side - 1).max(1) as i32,
|
||||||
|
Branch {
|
||||||
len: rng.gen_range(8, 16) * flip,
|
len: rng.gen_range(8, 16) * flip,
|
||||||
attr: Attr::generate(rng, locus),
|
attr: Attr::generate(rng, locus),
|
||||||
locus: (6 + rng.gen_range(0, 3)).min(locus),
|
locus: (6 + rng.gen_range(0, 3)).min(locus),
|
||||||
border: 4,
|
border: 4,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}))
|
},
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
@ -155,8 +170,17 @@ impl Archetype for House {
|
|||||||
let profile = Vec2::new(bound_offset.x, z);
|
let profile = Vec2::new(bound_offset.x, z);
|
||||||
|
|
||||||
let make_block = |r, g, b| {
|
let make_block = |r, g, b| {
|
||||||
let nz = self.noise.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
|
let nz = self
|
||||||
BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b).map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8))), 2)
|
.noise
|
||||||
|
.get(Vec3::new(center_offset.x, center_offset.y, z * 8));
|
||||||
|
BlockMask::new(
|
||||||
|
Block::new(
|
||||||
|
BlockKind::Normal,
|
||||||
|
Rgb::new(r, g, b)
|
||||||
|
.map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)),
|
||||||
|
),
|
||||||
|
2,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let facade_layer = 3;
|
let facade_layer = 3;
|
||||||
@ -168,7 +192,8 @@ impl Archetype for House {
|
|||||||
let log = make_block(60, 45, 30);
|
let log = make_block(60, 45, 30);
|
||||||
let floor = make_block(100, 75, 50);
|
let floor = make_block(100, 75, 50);
|
||||||
let wall = make_block(200, 180, 150).with_priority(facade_layer);
|
let wall = make_block(200, 180, 150).with_priority(facade_layer);
|
||||||
let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b).with_priority(facade_layer);
|
let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b)
|
||||||
|
.with_priority(facade_layer);
|
||||||
let empty = BlockMask::nothing();
|
let empty = BlockMask::nothing();
|
||||||
let internal = BlockMask::new(Block::empty(), structural_layer);
|
let internal = BlockMask::new(Block::empty(), structural_layer);
|
||||||
let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer);
|
let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer);
|
||||||
@ -176,13 +201,19 @@ impl Archetype for House {
|
|||||||
let ceil_height = 6;
|
let ceil_height = 6;
|
||||||
let lower_width = branch.locus - 1;
|
let lower_width = branch.locus - 1;
|
||||||
let upper_width = branch.locus;
|
let upper_width = branch.locus;
|
||||||
let width = if profile.y >= ceil_height { upper_width } else { lower_width };
|
let width = if profile.y >= ceil_height {
|
||||||
|
upper_width
|
||||||
|
} else {
|
||||||
|
lower_width
|
||||||
|
};
|
||||||
let foundation_height = 0 - (dist - width - 1).max(0);
|
let foundation_height = 0 - (dist - width - 1).max(0);
|
||||||
let roof_top = 8 + width;
|
let roof_top = 8 + width;
|
||||||
|
|
||||||
if let Pillar::Chimney(chimney_top) = branch.attr.pillar {
|
if let Pillar::Chimney(chimney_top) = branch.attr.pillar {
|
||||||
// Chimney shaft
|
// Chimney shaft
|
||||||
if center_offset.map(|e| e.abs()).reduce_max() == 0 && profile.y >= foundation_height + 1 {
|
if center_offset.map(|e| e.abs()).reduce_max() == 0
|
||||||
|
&& profile.y >= foundation_height + 1
|
||||||
|
{
|
||||||
return if profile.y == foundation_height + 1 {
|
return if profile.y == foundation_height + 1 {
|
||||||
fire
|
fire
|
||||||
} else {
|
} else {
|
||||||
@ -193,7 +224,10 @@ impl Archetype for House {
|
|||||||
// Chimney
|
// Chimney
|
||||||
if center_offset.map(|e| e.abs()).reduce_max() <= 1 && profile.y < chimney_top {
|
if center_offset.map(|e| e.abs()).reduce_max() <= 1 && profile.y < chimney_top {
|
||||||
// Fireplace
|
// Fireplace
|
||||||
if center_offset.product() == 0 && profile.y > foundation_height + 1 && profile.y <= foundation_height + 3 {
|
if center_offset.product() == 0
|
||||||
|
&& profile.y > foundation_height + 1
|
||||||
|
&& profile.y <= foundation_height + 3
|
||||||
|
{
|
||||||
return internal;
|
return internal;
|
||||||
} else {
|
} else {
|
||||||
return foundation;
|
return foundation;
|
||||||
@ -201,16 +235,20 @@ impl Archetype for House {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if profile.y <= foundation_height && dist < width + 3 { // Foundations
|
if profile.y <= foundation_height && dist < width + 3 {
|
||||||
|
// Foundations
|
||||||
if branch.attr.storey_fill.has_lower() {
|
if branch.attr.storey_fill.has_lower() {
|
||||||
if dist == width - 1 { // Floor lining
|
if dist == width - 1 {
|
||||||
|
// Floor lining
|
||||||
return log.with_priority(floor_layer);
|
return log.with_priority(floor_layer);
|
||||||
} else if dist < width - 1 && profile.y == foundation_height { // Floor
|
} else if dist < width - 1 && profile.y == foundation_height {
|
||||||
|
// Floor
|
||||||
return floor.with_priority(floor_layer);
|
return floor.with_priority(floor_layer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dist < width && profile.y < foundation_height && profile.y >= foundation_height - 3 { // Basement
|
if dist < width && profile.y < foundation_height && profile.y >= foundation_height - 3 {
|
||||||
|
// Basement
|
||||||
return internal;
|
return internal;
|
||||||
} else {
|
} else {
|
||||||
return foundation.with_priority(1);
|
return foundation.with_priority(1);
|
||||||
@ -218,14 +256,17 @@ impl Archetype for House {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Roofs and walls
|
// Roofs and walls
|
||||||
let do_roof_wall = |profile: Vec2<i32>, width, dist, bound_offset: Vec2<i32>, roof_top, mansard| {
|
let do_roof_wall =
|
||||||
|
|profile: Vec2<i32>, width, dist, bound_offset: Vec2<i32>, roof_top, mansard| {
|
||||||
// Roof
|
// Roof
|
||||||
|
|
||||||
let (roof_profile, roof_dist) = match &branch.attr.roof_style {
|
let (roof_profile, roof_dist) = match &branch.attr.roof_style {
|
||||||
RoofStyle::Hip => (Vec2::new(dist, profile.y), dist),
|
RoofStyle::Hip => (Vec2::new(dist, profile.y), dist),
|
||||||
RoofStyle::Gable => (profile, dist),
|
RoofStyle::Gable => (profile, dist),
|
||||||
RoofStyle::Rounded => {
|
RoofStyle::Rounded => {
|
||||||
let circular_dist = (bound_offset.map(|e| e.pow(4) as f32).sum().powf(0.25) - 0.5).ceil() as i32;
|
let circular_dist = (bound_offset.map(|e| e.pow(4) as f32).sum().powf(0.25)
|
||||||
|
- 0.5)
|
||||||
|
.ceil() as i32;
|
||||||
(Vec2::new(circular_dist, profile.y), circular_dist)
|
(Vec2::new(circular_dist, profile.y), circular_dist)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -240,7 +281,9 @@ impl Archetype for House {
|
|||||||
if profile.y == roof_level && roof_dist <= width + 2 {
|
if profile.y == roof_level && roof_dist <= width + 2 {
|
||||||
let is_ribbing = ((profile.y - ceil_height) % 3 == 0 && self.roof_ribbing)
|
let is_ribbing = ((profile.y - ceil_height) % 3 == 0 && self.roof_ribbing)
|
||||||
|| (bound_offset.x == bound_offset.y && self.roof_ribbing_diagonal);
|
|| (bound_offset.x == bound_offset.y && self.roof_ribbing_diagonal);
|
||||||
if (roof_profile.x == 0 && mansard == 0) || roof_dist == width + 2 || is_ribbing { // Eaves
|
if (roof_profile.x == 0 && mansard == 0) || roof_dist == width + 2 || is_ribbing
|
||||||
|
{
|
||||||
|
// Eaves
|
||||||
return Some(log);
|
return Some(log);
|
||||||
} else {
|
} else {
|
||||||
return Some(roof);
|
return Some(roof);
|
||||||
@ -250,7 +293,8 @@ impl Archetype for House {
|
|||||||
// Wall
|
// Wall
|
||||||
|
|
||||||
if dist == width && profile.y < roof_level {
|
if dist == width && profile.y < roof_level {
|
||||||
if bound_offset.x == bound_offset.y || profile.y == ceil_height { // Support beams
|
if bound_offset.x == bound_offset.y || profile.y == ceil_height {
|
||||||
|
// Support beams
|
||||||
return Some(log);
|
return Some(log);
|
||||||
} else if !branch.attr.storey_fill.has_lower() && profile.y < ceil_height {
|
} else if !branch.attr.storey_fill.has_lower() && profile.y < ceil_height {
|
||||||
return Some(empty);
|
return Some(empty);
|
||||||
@ -269,12 +313,15 @@ impl Archetype for House {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let window_bounds = Aabr {
|
let window_bounds = Aabr {
|
||||||
min: (frame_bounds.min + 1).map2(frame_bounds.center(), |a, b| a.min(b)),
|
min: (frame_bounds.min + 1)
|
||||||
max: (frame_bounds.max - 1).map2(frame_bounds.center(), |a, b| a.max(b)),
|
.map2(frame_bounds.center(), |a, b| a.min(b)),
|
||||||
|
max: (frame_bounds.max - 1)
|
||||||
|
.map2(frame_bounds.center(), |a, b| a.max(b)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Window
|
// Window
|
||||||
if (frame_bounds.size() + 1).reduce_min() > 2 { // Window frame is large enough for a window
|
if (frame_bounds.size() + 1).reduce_min() > 2 {
|
||||||
|
// Window frame is large enough for a window
|
||||||
let surface_pos = Vec2::new(bound_offset.x, profile.y);
|
let surface_pos = Vec2::new(bound_offset.x, profile.y);
|
||||||
if window_bounds.contains_point(surface_pos) {
|
if window_bounds.contains_point(surface_pos) {
|
||||||
return Some(internal);
|
return Some(internal);
|
||||||
@ -284,7 +331,8 @@ impl Archetype for House {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wall
|
// Wall
|
||||||
return Some(if branch.attr.central_supports && profile.x == 0 { // Support beams
|
return Some(if branch.attr.central_supports && profile.x == 0 {
|
||||||
|
// Support beams
|
||||||
log.with_priority(structural_layer)
|
log.with_priority(structural_layer)
|
||||||
} else {
|
} else {
|
||||||
wall
|
wall
|
||||||
@ -292,11 +340,14 @@ impl Archetype for House {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dist < width { // Internals
|
if dist < width {
|
||||||
|
// Internals
|
||||||
if profile.y == ceil_height {
|
if profile.y == ceil_height {
|
||||||
if profile.x == 0 {// Rafters
|
if profile.x == 0 {
|
||||||
|
// Rafters
|
||||||
return Some(log);
|
return Some(log);
|
||||||
} else if branch.attr.storey_fill.has_upper() { // Ceiling
|
} else if branch.attr.storey_fill.has_upper() {
|
||||||
|
// Ceiling
|
||||||
return Some(floor);
|
return Some(floor);
|
||||||
}
|
}
|
||||||
} else if (!branch.attr.storey_fill.has_lower() && profile.y < ceil_height)
|
} else if (!branch.attr.storey_fill.has_lower() && profile.y < ceil_height)
|
||||||
@ -313,7 +364,14 @@ impl Archetype for House {
|
|||||||
|
|
||||||
let mut cblock = empty;
|
let mut cblock = empty;
|
||||||
|
|
||||||
if let Some(block) = do_roof_wall(profile, width, dist, bound_offset, roof_top, branch.attr.mansard) {
|
if let Some(block) = do_roof_wall(
|
||||||
|
profile,
|
||||||
|
width,
|
||||||
|
dist,
|
||||||
|
bound_offset,
|
||||||
|
roof_top,
|
||||||
|
branch.attr.mansard,
|
||||||
|
) {
|
||||||
cblock = cblock.resolve_with(block);
|
cblock = cblock.resolve_with(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +379,14 @@ impl Archetype for House {
|
|||||||
let profile = Vec2::new(center_offset.x.abs(), profile.y);
|
let profile = Vec2::new(center_offset.x.abs(), profile.y);
|
||||||
let dist = center_offset.map(|e| e.abs()).reduce_max();
|
let dist = center_offset.map(|e| e.abs()).reduce_max();
|
||||||
|
|
||||||
if let Some(block) = do_roof_wall(profile, 4, dist, center_offset.map(|e| e.abs()), tower_top, branch.attr.mansard) {
|
if let Some(block) = do_roof_wall(
|
||||||
|
profile,
|
||||||
|
4,
|
||||||
|
dist,
|
||||||
|
center_offset.map(|e| e.abs()),
|
||||||
|
tower_top,
|
||||||
|
branch.attr.mansard,
|
||||||
|
) {
|
||||||
cblock = cblock.resolve_with(block);
|
cblock = cblock.resolve_with(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
use vek::*;
|
use super::{super::skeleton::*, Archetype};
|
||||||
use rand::prelude::*;
|
use crate::site::BlockMask;
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
vol::Vox,
|
vol::Vox,
|
||||||
};
|
};
|
||||||
use super::{
|
use rand::prelude::*;
|
||||||
Archetype,
|
use vek::*;
|
||||||
super::skeleton::*,
|
|
||||||
};
|
|
||||||
use crate::site::BlockMask;
|
|
||||||
|
|
||||||
pub struct Keep;
|
pub struct Keep;
|
||||||
|
|
||||||
@ -26,13 +23,18 @@ impl Archetype for Keep {
|
|||||||
locus: 5 + rng.gen_range(0, 5),
|
locus: 5 + rng.gen_range(0, 5),
|
||||||
border: 3,
|
border: 3,
|
||||||
children: (0..rng.gen_range(0, 4))
|
children: (0..rng.gen_range(0, 4))
|
||||||
.map(|_| (rng.gen_range(-5, len + 5).clamped(0, len.max(1) - 1), Branch {
|
.map(|_| {
|
||||||
|
(
|
||||||
|
rng.gen_range(-5, len + 5).clamped(0, len.max(1) - 1),
|
||||||
|
Branch {
|
||||||
len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 },
|
len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 },
|
||||||
attr: Self::Attr::default(),
|
attr: Self::Attr::default(),
|
||||||
locus: 5 + rng.gen_range(0, 3),
|
locus: 5 + rng.gen_range(0, 3),
|
||||||
border: 3,
|
border: 3,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}))
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -50,9 +52,8 @@ impl Archetype for Keep {
|
|||||||
) -> BlockMask {
|
) -> BlockMask {
|
||||||
let profile = Vec2::new(bound_offset.x, z);
|
let profile = Vec2::new(bound_offset.x, z);
|
||||||
|
|
||||||
let make_block = |r, g, b| {
|
let make_block =
|
||||||
BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b)), 2)
|
|r, g, b| BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b)), 2);
|
||||||
};
|
|
||||||
|
|
||||||
let foundation = make_block(100, 100, 100);
|
let foundation = make_block(100, 100, 100);
|
||||||
let log = make_block(60, 45, 30);
|
let log = make_block(60, 45, 30);
|
||||||
@ -65,7 +66,8 @@ impl Archetype for Keep {
|
|||||||
let roof_height = 12 + width;
|
let roof_height = 12 + width;
|
||||||
let ceil_height = 16;
|
let ceil_height = 16;
|
||||||
|
|
||||||
if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { // Foundations
|
if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 {
|
||||||
|
// Foundations
|
||||||
foundation
|
foundation
|
||||||
} else if profile.y == ceil_height && dist < rampart_width {
|
} else if profile.y == ceil_height && dist < rampart_width {
|
||||||
roof
|
roof
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
pub mod house;
|
pub mod house;
|
||||||
pub mod keep;
|
pub mod keep;
|
||||||
|
|
||||||
use vek::*;
|
|
||||||
use rand::prelude::*;
|
|
||||||
use super::skeleton::*;
|
use super::skeleton::*;
|
||||||
use crate::site::BlockMask;
|
use crate::site::BlockMask;
|
||||||
|
use rand::prelude::*;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
pub trait Archetype {
|
pub trait Archetype {
|
||||||
type Attr;
|
type Attr;
|
||||||
|
|
||||||
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>) where Self: Sized;
|
fn generate<R: Rng>(rng: &mut R) -> (Self, Skeleton<Self::Attr>)
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
dist: i32,
|
dist: i32,
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
mod skeleton;
|
|
||||||
mod archetype;
|
mod archetype;
|
||||||
|
mod skeleton;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use self::archetype::Archetype;
|
pub use self::archetype::Archetype;
|
||||||
|
|
||||||
use vek::*;
|
|
||||||
use rand::prelude::*;
|
|
||||||
use self::skeleton::*;
|
use self::skeleton::*;
|
||||||
use common::terrain::Block;
|
use common::terrain::Block;
|
||||||
|
use rand::prelude::*;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
pub type HouseBuilding = Building<archetype::house::House>;
|
pub type HouseBuilding = Building<archetype::house::House>;
|
||||||
|
|
||||||
@ -19,7 +19,8 @@ pub struct Building<A: Archetype> {
|
|||||||
|
|
||||||
impl<A: Archetype> Building<A> {
|
impl<A: Archetype> Building<A> {
|
||||||
pub fn generate(rng: &mut impl Rng, origin: Vec3<i32>) -> Self
|
pub fn generate(rng: &mut impl Rng, origin: Vec3<i32>) -> Self
|
||||||
where A: Sized
|
where
|
||||||
|
A: Sized,
|
||||||
{
|
{
|
||||||
let len = rng.gen_range(-8, 12).max(0);
|
let len = rng.gen_range(-8, 12).max(0);
|
||||||
let (archetype, skel) = A::generate(rng);
|
let (archetype, skel) = A::generate(rng);
|
||||||
@ -48,8 +49,11 @@ impl<A: Archetype> Building<A> {
|
|||||||
|
|
||||||
pub fn sample(&self, pos: Vec3<i32>) -> Option<Block> {
|
pub fn sample(&self, pos: Vec3<i32>) -> Option<Block> {
|
||||||
let rpos = pos - self.origin;
|
let rpos = pos - self.origin;
|
||||||
self.skel.sample_closest(rpos.into(), |dist, bound_offset, center_offset, branch| {
|
self.skel
|
||||||
self.archetype.draw(dist, bound_offset, center_offset, rpos.z, branch)
|
.sample_closest(rpos.into(), |dist, bound_offset, center_offset, branch| {
|
||||||
}).finish()
|
self.archetype
|
||||||
|
.draw(dist, bound_offset, center_offset, rpos.z, branch)
|
||||||
|
})
|
||||||
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use vek::*;
|
|
||||||
use crate::site::BlockMask;
|
use crate::site::BlockMask;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub enum Ori {
|
pub enum Ori {
|
||||||
@ -32,7 +32,14 @@ pub struct Branch<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Branch<T> {
|
impl<T> Branch<T> {
|
||||||
fn for_each<'a>(&'a self, node: Vec2<i32>, ori: Ori, is_child: bool, parent_locus: i32, f: &mut impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32)) {
|
fn for_each<'a>(
|
||||||
|
&'a self,
|
||||||
|
node: Vec2<i32>,
|
||||||
|
ori: Ori,
|
||||||
|
is_child: bool,
|
||||||
|
parent_locus: i32,
|
||||||
|
f: &mut impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32),
|
||||||
|
) {
|
||||||
f(node, ori, self, is_child, parent_locus);
|
f(node, ori, self, is_child, parent_locus);
|
||||||
for (offset, child) in &self.children {
|
for (offset, child) in &self.children {
|
||||||
child.for_each(node + ori.dir() * *offset, ori.flip(), true, self.locus, f);
|
child.for_each(node + ori.dir() * *offset, ori.flip(), true, self.locus, f);
|
||||||
@ -48,7 +55,8 @@ pub struct Skeleton<T> {
|
|||||||
|
|
||||||
impl<T> Skeleton<T> {
|
impl<T> Skeleton<T> {
|
||||||
pub fn for_each<'a>(&'a self, mut f: impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32)) {
|
pub fn for_each<'a>(&'a self, mut f: impl FnMut(Vec2<i32>, Ori, &'a Branch<T>, bool, i32)) {
|
||||||
self.root.for_each(self.ori.dir() * self.offset, self.ori, false, 0, &mut f);
|
self.root
|
||||||
|
.for_each(self.ori.dir() * self.offset, self.ori, false, 0, &mut f);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bounds(&self) -> Aabr<i32> {
|
pub fn bounds(&self) -> Aabr<i32> {
|
||||||
@ -64,24 +72,35 @@ impl<T> Skeleton<T> {
|
|||||||
bounds
|
bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_closest(&self, pos: Vec2<i32>, mut f: impl FnMut(i32, Vec2<i32>, Vec2<i32>, &Branch<T>) -> BlockMask) -> BlockMask {
|
pub fn sample_closest(
|
||||||
|
&self,
|
||||||
|
pos: Vec2<i32>,
|
||||||
|
mut f: impl FnMut(i32, Vec2<i32>, Vec2<i32>, &Branch<T>) -> BlockMask,
|
||||||
|
) -> BlockMask {
|
||||||
let mut min = None::<(_, BlockMask)>;
|
let mut min = None::<(_, BlockMask)>;
|
||||||
self.for_each(|node, ori, branch, is_child, parent_locus| {
|
self.for_each(|node, ori, branch, is_child, parent_locus| {
|
||||||
let node2 = node + ori.dir() * branch.len;
|
let node2 = node + ori.dir() * branch.len;
|
||||||
let node = node + if is_child { ori.dir() * branch.len.signum() * (branch.locus - parent_locus).clamped(0, branch.len.abs()) } else { Vec2::zero() };
|
let node = node
|
||||||
let bounds = Aabr::new_empty(node)
|
+ if is_child {
|
||||||
.expanded_to_contain_point(node2);
|
ori.dir()
|
||||||
|
* branch.len.signum()
|
||||||
|
* (branch.locus - parent_locus).clamped(0, branch.len.abs())
|
||||||
|
} else {
|
||||||
|
Vec2::zero()
|
||||||
|
};
|
||||||
|
let bounds = Aabr::new_empty(node).expanded_to_contain_point(node2);
|
||||||
let bound_offset = if ori == Ori::East {
|
let bound_offset = if ori == Ori::East {
|
||||||
Vec2::new(
|
Vec2::new(
|
||||||
node.y - pos.y,
|
node.y - pos.y,
|
||||||
pos.x - pos.x.clamped(bounds.min.x, bounds.max.x)
|
pos.x - pos.x.clamped(bounds.min.x, bounds.max.x),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Vec2::new(
|
Vec2::new(
|
||||||
node.x - pos.x,
|
node.x - pos.x,
|
||||||
pos.y - pos.y.clamped(bounds.min.y, bounds.max.y)
|
pos.y - pos.y.clamped(bounds.min.y, bounds.max.y),
|
||||||
)
|
)
|
||||||
}.map(|e| e.abs());
|
}
|
||||||
|
.map(|e| e.abs());
|
||||||
let center_offset = if ori == Ori::East {
|
let center_offset = if ori == Ori::East {
|
||||||
Vec2::new(pos.y - bounds.center().y, pos.x - bounds.center().x)
|
Vec2::new(pos.y - bounds.center().y, pos.x - bounds.center().x)
|
||||||
} else {
|
} else {
|
||||||
@ -89,10 +108,13 @@ impl<T> Skeleton<T> {
|
|||||||
};
|
};
|
||||||
let dist = bound_offset.reduce_max();
|
let dist = bound_offset.reduce_max();
|
||||||
let dist_locus = dist - branch.locus;
|
let dist_locus = dist - branch.locus;
|
||||||
if !is_child || match ori {
|
if !is_child
|
||||||
|
|| match ori {
|
||||||
Ori::East => (pos.x - node.x) * branch.len.signum() >= 0,
|
Ori::East => (pos.x - node.x) * branch.len.signum() >= 0,
|
||||||
Ori::North => (pos.y - node.y) * branch.len.signum() >= 0,
|
Ori::North => (pos.y - node.y) * branch.len.signum() >= 0,
|
||||||
} || true {
|
}
|
||||||
|
|| true
|
||||||
|
{
|
||||||
let new_bm = f(dist, bound_offset, center_offset, branch);
|
let new_bm = f(dist, bound_offset, center_offset, branch);
|
||||||
min = min
|
min = min
|
||||||
.map(|(_, bm)| (dist_locus, bm.resolve_with(new_bm)))
|
.map(|(_, bm)| (dist_locus, bm.resolve_with(new_bm)))
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
mod building;
|
mod building;
|
||||||
|
|
||||||
|
use self::building::HouseBuilding;
|
||||||
|
use super::SpawnRules;
|
||||||
use crate::{
|
use crate::{
|
||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
sim::{SimChunk, WorldSim},
|
sim::{SimChunk, WorldSim},
|
||||||
util::{Grid, RandomField, Sampler, StructureGen2d},
|
util::{Grid, RandomField, Sampler, StructureGen2d},
|
||||||
};
|
};
|
||||||
use self::building::HouseBuilding;
|
|
||||||
use super::SpawnRules;
|
|
||||||
use common::{
|
use common::{
|
||||||
astar::Astar,
|
astar::Astar,
|
||||||
|
generation::ChunkSupplement,
|
||||||
path::Path,
|
path::Path,
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
terrain::{Block, BlockKind, TerrainChunkSize},
|
|
||||||
vol::{BaseVol, RectSizedVol, RectVolSize, ReadVol, WriteVol, Vox},
|
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
generation::ChunkSupplement,
|
terrain::{Block, BlockKind, TerrainChunkSize},
|
||||||
|
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||||
};
|
};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
@ -74,11 +74,8 @@ pub fn center_of(p: [Vec2<f32>; 3]) -> Vec2<f32> {
|
|||||||
|
|
||||||
impl WorldSim {
|
impl WorldSim {
|
||||||
fn can_host_settlement(&self, pos: Vec2<i32>) -> bool {
|
fn can_host_settlement(&self, pos: Vec2<i32>) -> bool {
|
||||||
self
|
self.get(pos)
|
||||||
.get(pos)
|
.map(|chunk| !chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake())
|
||||||
.map(|chunk| {
|
|
||||||
!chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake()
|
|
||||||
})
|
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
&& self
|
&& self
|
||||||
.get_gradient_approx(pos)
|
.get_gradient_approx(pos)
|
||||||
@ -172,7 +169,8 @@ impl Settlement {
|
|||||||
let cpos = wpos.map(|e| e.div_euclid(TerrainChunkSize::RECT_SIZE.x as i32));
|
let cpos = wpos.map(|e| e.div_euclid(TerrainChunkSize::RECT_SIZE.x as i32));
|
||||||
!sim.can_host_settlement(cpos)
|
!sim.can_host_settlement(cpos)
|
||||||
})
|
})
|
||||||
|| rng.gen_range(0, 16) == 0 // Randomly consider some tiles inaccessible
|
|| rng.gen_range(0, 16) == 0
|
||||||
|
// Randomly consider some tiles inaccessible
|
||||||
{
|
{
|
||||||
self.land.set(tile, hazard);
|
self.land.set(tile, hazard);
|
||||||
}
|
}
|
||||||
@ -328,35 +326,49 @@ impl Settlement {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
for tile in Spiral2d::new().map(|offs| town_center + offs).take(16usize.pow(2)) {
|
for tile in Spiral2d::new()
|
||||||
|
.map(|offs| town_center + offs)
|
||||||
|
.take(16usize.pow(2))
|
||||||
|
{
|
||||||
// This is a stupid way to decide how to place buildings
|
// This is a stupid way to decide how to place buildings
|
||||||
for _ in 0..ctx.rng.gen_range(2, 5) {
|
for _ in 0..ctx.rng.gen_range(2, 5) {
|
||||||
for _ in 0..25 {
|
for _ in 0..25 {
|
||||||
let house_pos = tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2)
|
let house_pos = tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2)
|
||||||
+ Vec2::<i32>::zero().map(|_| ctx.rng.gen_range(-(AREA_SIZE as i32) / 2, AREA_SIZE as i32 / 2));
|
+ Vec2::<i32>::zero().map(|_| {
|
||||||
|
ctx.rng
|
||||||
|
.gen_range(-(AREA_SIZE as i32) / 2, AREA_SIZE as i32 / 2)
|
||||||
|
});
|
||||||
|
|
||||||
let tile_pos = house_pos.map(|e| e.div_euclid(AREA_SIZE as i32));
|
let tile_pos = house_pos.map(|e| e.div_euclid(AREA_SIZE as i32));
|
||||||
if !matches!(self.land.plot_at(tile_pos), Some(Plot::Town))
|
if !matches!(self.land.plot_at(tile_pos), Some(Plot::Town))
|
||||||
|| self.land.tile_at(tile_pos).map(|t| t.contains(WayKind::Path)).unwrap_or(true)
|
|| self
|
||||||
|
.land
|
||||||
|
.tile_at(tile_pos)
|
||||||
|
.map(|t| t.contains(WayKind::Path))
|
||||||
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let structure = Structure {
|
let structure = Structure {
|
||||||
kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new(
|
kind: StructureKind::House(HouseBuilding::generate(
|
||||||
|
ctx.rng,
|
||||||
|
Vec3::new(
|
||||||
house_pos.x,
|
house_pos.x,
|
||||||
house_pos.y,
|
house_pos.y,
|
||||||
ctx.sim
|
ctx.sim
|
||||||
.and_then(|sim| sim.get_alt_approx(self.origin + house_pos))
|
.and_then(|sim| sim.get_alt_approx(self.origin + house_pos))
|
||||||
.unwrap_or(0.0)
|
.unwrap_or(0.0)
|
||||||
.ceil() as i32,
|
.ceil() as i32,
|
||||||
))),
|
),
|
||||||
|
)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let bounds = structure.bounds_2d();
|
let bounds = structure.bounds_2d();
|
||||||
|
|
||||||
// Check for collision with other structures
|
// Check for collision with other structures
|
||||||
if self.structures
|
if self
|
||||||
|
.structures
|
||||||
.iter()
|
.iter()
|
||||||
.any(|s| s.bounds_2d().collides_with_aabr(bounds))
|
.any(|s| s.bounds_2d().collides_with_aabr(bounds))
|
||||||
{
|
{
|
||||||
@ -385,12 +397,13 @@ impl Settlement {
|
|||||||
|
|
||||||
// Farmhouses
|
// Farmhouses
|
||||||
// for _ in 0..ctx.rng.gen_range(1, 3) {
|
// for _ in 0..ctx.rng.gen_range(1, 3) {
|
||||||
// let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2)
|
// let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32
|
||||||
// + Vec2::new(ctx.rng.gen_range(-16, 16), ctx.rng.gen_range(-16, 16));
|
// / 2) + Vec2::new(ctx.rng.gen_range(-16, 16),
|
||||||
|
// ctx.rng.gen_range(-16, 16));
|
||||||
|
|
||||||
// self.structures.push(Structure {
|
// self.structures.push(Structure {
|
||||||
// kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new(
|
// kind: StructureKind::House(HouseBuilding::generate(ctx.rng,
|
||||||
// house_pos.x,
|
// Vec3::new( house_pos.x,
|
||||||
// house_pos.y,
|
// house_pos.y,
|
||||||
// ctx.sim
|
// ctx.sim
|
||||||
// .and_then(|sim| sim.get_alt_approx(self.origin + house_pos))
|
// .and_then(|sim| sim.get_alt_approx(self.origin + house_pos))
|
||||||
@ -421,11 +434,15 @@ impl Settlement {
|
|||||||
let field = self.land.new_plot(Plot::Field {
|
let field = self.land.new_plot(Plot::Field {
|
||||||
farm,
|
farm,
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
crop: match rng.gen_range(0, 5) {
|
crop: match rng.gen_range(0, 8) {
|
||||||
0 => Crop::Corn,
|
0 => Crop::Corn,
|
||||||
1 => Crop::Wheat,
|
1 => Crop::Wheat,
|
||||||
2 => Crop::Cabbage,
|
2 => Crop::Cabbage,
|
||||||
3 => Crop::Pumpkin,
|
3 => Crop::Pumpkin,
|
||||||
|
4 => Crop::Flax,
|
||||||
|
5 => Crop::Carrot,
|
||||||
|
6 => Crop::Tomato,
|
||||||
|
7 => Crop::Radish,
|
||||||
_ => Crop::Sunflower,
|
_ => Crop::Sunflower,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -447,7 +464,8 @@ impl Settlement {
|
|||||||
|
|
||||||
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
|
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
|
||||||
SpawnRules {
|
SpawnRules {
|
||||||
trees: self.land
|
trees: self
|
||||||
|
.land
|
||||||
.get_at_block(wpos - self.origin)
|
.get_at_block(wpos - self.origin)
|
||||||
.plot
|
.plot
|
||||||
.map(|p| if let Plot::Hazard = p { true } else { false })
|
.map(|p| if let Plot::Hazard = p { true } else { false })
|
||||||
@ -484,19 +502,28 @@ impl Settlement {
|
|||||||
|
|
||||||
let noisy_color = |col: Rgb<u8>, factor: u32| {
|
let noisy_color = |col: Rgb<u8>, factor: u32| {
|
||||||
let nz = self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, surface_z));
|
let nz = self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, surface_z));
|
||||||
col.map(|e| (e as u32 + nz % (factor * 2)).saturating_sub(factor).min(255) as u8)
|
col.map(|e| {
|
||||||
|
(e as u32 + nz % (factor * 2))
|
||||||
|
.saturating_sub(factor)
|
||||||
|
.min(255) as u8
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
if let Some((WayKind::Path, dist, nearest)) = sample.way {
|
if let Some((WayKind::Path, dist, nearest)) = sample.way {
|
||||||
let inset = -1;
|
let inset = -1;
|
||||||
|
|
||||||
// Try to use the column at the centre of the path for sampling to make them flatter
|
// Try to use the column at the centre of the path for sampling to make them
|
||||||
let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)).unwrap_or(col_sample);
|
// flatter
|
||||||
|
let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos))
|
||||||
|
.unwrap_or(col_sample);
|
||||||
let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist {
|
let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist {
|
||||||
(
|
(
|
||||||
((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * 5.0,
|
((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * 5.0,
|
||||||
((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) * (col.riverless_alt + 5.0 - col.alt).max(0.0) * 1.75 + 3.0) as i32,
|
((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs())
|
||||||
|
* (col.riverless_alt + 5.0 - col.alt).max(0.0)
|
||||||
|
* 1.75
|
||||||
|
+ 3.0) as i32,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(0.0, 3)
|
(0.0, 3)
|
||||||
@ -528,10 +555,15 @@ impl Settlement {
|
|||||||
Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)),
|
Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)),
|
||||||
Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)),
|
Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)),
|
||||||
Some(Plot::Water) => Some(Rgb::new(100, 150, 250)),
|
Some(Plot::Water) => Some(Rgb::new(100, 150, 250)),
|
||||||
Some(Plot::Town) => Some(Rgb::new(150, 110, 60)
|
Some(Plot::Town) => {
|
||||||
.map2(Rgb::iota(), |e: u8, i: i32| e
|
Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| {
|
||||||
.saturating_add((self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 16) as u8)
|
e.saturating_add(
|
||||||
.saturating_sub(8))),
|
(self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 16)
|
||||||
|
as u8,
|
||||||
|
)
|
||||||
|
.saturating_sub(8)
|
||||||
|
}))
|
||||||
|
},
|
||||||
Some(Plot::Field { seed, crop, .. }) => {
|
Some(Plot::Field { seed, crop, .. }) => {
|
||||||
let furrow_dirs = [
|
let furrow_dirs = [
|
||||||
Vec2::new(1, 0),
|
Vec2::new(1, 0),
|
||||||
@ -542,19 +574,44 @@ impl Settlement {
|
|||||||
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
|
||||||
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
|
let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2;
|
||||||
|
|
||||||
let roll = |seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n;
|
let roll = |seed, n| {
|
||||||
|
self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n
|
||||||
|
};
|
||||||
|
|
||||||
let dirt = Rgb::new(80, 55, 35).map(|e| e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32) as u8);
|
let dirt = Rgb::new(80, 55, 35).map(|e| {
|
||||||
let mound = Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32)) % 32) as u8);
|
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32)
|
||||||
|
as u8
|
||||||
|
});
|
||||||
|
let mound =
|
||||||
|
Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| {
|
||||||
|
e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32))
|
||||||
|
% 32) as u8
|
||||||
|
});
|
||||||
|
|
||||||
if in_furrow {
|
if in_furrow {
|
||||||
if roll(0, 5) == 0 {
|
if roll(0, 5) == 0 {
|
||||||
surface_block = match crop {
|
surface_block = match crop {
|
||||||
Crop::Corn => Some(BlockKind::Corn),
|
Crop::Corn => Some(BlockKind::Corn),
|
||||||
Crop::Wheat if roll(1, 2) == 0 => Some(BlockKind::WheatYellow),
|
Crop::Wheat if roll(1, 2) == 0 => {
|
||||||
|
Some(BlockKind::WheatYellow)
|
||||||
|
},
|
||||||
Crop::Wheat => Some(BlockKind::WheatGreen),
|
Crop::Wheat => Some(BlockKind::WheatGreen),
|
||||||
Crop::Cabbage if roll(2, 2) == 0 => Some(BlockKind::Cabbage),
|
Crop::Cabbage if roll(2, 2) == 0 => {
|
||||||
Crop::Pumpkin if roll(3, 2) == 0 => Some(BlockKind::Pumpkin),
|
Some(BlockKind::Cabbage)
|
||||||
|
},
|
||||||
|
Crop::Pumpkin if roll(3, 2) == 0 => {
|
||||||
|
Some(BlockKind::Pumpkin)
|
||||||
|
},
|
||||||
|
Crop::Flax if roll(4, 100) < 80 => Some(BlockKind::Flax),
|
||||||
|
Crop::Carrot if roll(5, 100) < 80 => {
|
||||||
|
Some(BlockKind::Carrot)
|
||||||
|
},
|
||||||
|
Crop::Tomato if roll(6, 100) < 80 => {
|
||||||
|
Some(BlockKind::Tomato)
|
||||||
|
},
|
||||||
|
Crop::Radish if roll(7, 100) < 80 => {
|
||||||
|
Some(BlockKind::Radish)
|
||||||
|
},
|
||||||
Crop::Sunflower => Some(BlockKind::Sunflower),
|
Crop::Sunflower => Some(BlockKind::Sunflower),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -562,17 +619,15 @@ impl Settlement {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if roll(0, 30) == 0 {
|
if roll(0, 30) == 0 {
|
||||||
surface_block = Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
|
surface_block =
|
||||||
|
Some(Block::new(BlockKind::ShortGrass, Rgb::white()));
|
||||||
} else if roll(0, 30) == 0 {
|
} else if roll(0, 30) == 0 {
|
||||||
surface_block = Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
|
surface_block =
|
||||||
|
Some(Block::new(BlockKind::MediumGrass, Rgb::white()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(if in_furrow {
|
Some(if in_furrow { dirt } else { mound })
|
||||||
dirt
|
|
||||||
} else {
|
|
||||||
mound
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
@ -589,7 +644,10 @@ impl Settlement {
|
|||||||
vol.set(pos, Block::empty());
|
vol.set(pos, Block::empty());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vol.set(pos, Block::new(BlockKind::Normal, noisy_color(color, 4)));
|
vol.set(
|
||||||
|
pos,
|
||||||
|
Block::new(BlockKind::Normal, noisy_color(color, 4)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -613,9 +671,7 @@ impl Settlement {
|
|||||||
} as i32;
|
} as i32;
|
||||||
|
|
||||||
for z in z_offset..12 {
|
for z in z_offset..12 {
|
||||||
if dist / WayKind::Wall.width()
|
if dist / WayKind::Wall.width() < ((1.0 - z as f32 / 12.0) * 2.0).min(1.0) {
|
||||||
< ((1.0 - z as f32 / 12.0) * 2.0).min(1.0)
|
|
||||||
{
|
|
||||||
vol.set(
|
vol.set(
|
||||||
Vec3::new(offs.x, offs.y, surface_z + z),
|
Vec3::new(offs.x, offs.y, surface_z + z),
|
||||||
Block::new(BlockKind::Normal, color),
|
Block::new(BlockKind::Normal, color),
|
||||||
@ -655,13 +711,16 @@ impl Settlement {
|
|||||||
|
|
||||||
for x in bounds.min.x..bounds.max.x + 1 {
|
for x in bounds.min.x..bounds.max.x + 1 {
|
||||||
for y in bounds.min.y..bounds.max.y + 1 {
|
for y in bounds.min.y..bounds.max.y + 1 {
|
||||||
let col = if let Some(col) = get_column(self.origin + Vec2::new(x, y) - wpos2d) {
|
let col = if let Some(col) =
|
||||||
|
get_column(self.origin + Vec2::new(x, y) - wpos2d)
|
||||||
|
{
|
||||||
col
|
col
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
for z in bounds.min.z.min(col.alt.floor() as i32 - 1)..bounds.max.z + 1 {
|
for z in bounds.min.z.min(col.alt.floor() as i32 - 1)..bounds.max.z + 1
|
||||||
|
{
|
||||||
let rpos = Vec3::new(x, y, z);
|
let rpos = Vec3::new(x, y, z);
|
||||||
let wpos = Vec3::from(self.origin) + rpos;
|
let wpos = Vec3::from(self.origin) + rpos;
|
||||||
let coffs = wpos - Vec3::from(wpos2d);
|
let coffs = wpos - Vec3::from(wpos2d);
|
||||||
@ -705,10 +764,12 @@ impl Settlement {
|
|||||||
Some(Plot::Dirt) => return Some(Rgb::new(90, 70, 50)),
|
Some(Plot::Dirt) => return Some(Rgb::new(90, 70, 50)),
|
||||||
Some(Plot::Grass) => return Some(Rgb::new(100, 200, 0)),
|
Some(Plot::Grass) => return Some(Rgb::new(100, 200, 0)),
|
||||||
Some(Plot::Water) => return Some(Rgb::new(100, 150, 250)),
|
Some(Plot::Water) => return Some(Rgb::new(100, 150, 250)),
|
||||||
Some(Plot::Town) => return Some(Rgb::new(150, 110, 60)
|
Some(Plot::Town) => {
|
||||||
.map2(Rgb::iota(), |e: u8, i: i32| e
|
return Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| {
|
||||||
.saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8)
|
e.saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8)
|
||||||
.saturating_sub(8))),
|
.saturating_sub(8)
|
||||||
|
}));
|
||||||
|
},
|
||||||
Some(Plot::Field { seed, .. }) => {
|
Some(Plot::Field { seed, .. }) => {
|
||||||
let furrow_dirs = [
|
let furrow_dirs = [
|
||||||
Vec2::new(1, 0),
|
Vec2::new(1, 0),
|
||||||
@ -741,6 +802,10 @@ pub enum Crop {
|
|||||||
Wheat,
|
Wheat,
|
||||||
Cabbage,
|
Cabbage,
|
||||||
Pumpkin,
|
Pumpkin,
|
||||||
|
Flax,
|
||||||
|
Carrot,
|
||||||
|
Tomato,
|
||||||
|
Radish,
|
||||||
Sunflower,
|
Sunflower,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -751,7 +816,11 @@ pub enum Plot {
|
|||||||
Grass,
|
Grass,
|
||||||
Water,
|
Water,
|
||||||
Town,
|
Town,
|
||||||
Field { farm: Id<Farm>, seed: u32, crop: Crop },
|
Field {
|
||||||
|
farm: Id<Farm>,
|
||||||
|
seed: u32,
|
||||||
|
crop: Crop,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const CARDINALS: [Vec2<i32>; 4] = [
|
const CARDINALS: [Vec2<i32>; 4] = [
|
||||||
@ -855,7 +924,8 @@ impl Land {
|
|||||||
let proj_point = line.projected_point(pos.map(|e| e as f32));
|
let proj_point = line.projected_point(pos.map(|e| e as f32));
|
||||||
let dist = proj_point.distance(pos.map(|e| e as f32));
|
let dist = proj_point.distance(pos.map(|e| e as f32));
|
||||||
if dist < way.width() {
|
if dist < way.width() {
|
||||||
sample.way = sample.way
|
sample.way = sample
|
||||||
|
.way
|
||||||
.filter(|(_, d, _)| *d < dist)
|
.filter(|(_, d, _)| *d < dist)
|
||||||
.or(Some((way, dist, proj_point)));
|
.or(Some((way, dist, proj_point)));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user