mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
fmt
This commit is contained in:
parent
02d86f0fb0
commit
862cd5fe49
897
Cargo.lock
generated
897
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -33,12 +33,10 @@ impl<T> FromIterator<T> for Path<T> {
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for Path<T> {
|
||||
type Item = T;
|
||||
type IntoIter = std::vec::IntoIter<T>;
|
||||
type Item = T;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.nodes.into_iter()
|
||||
}
|
||||
fn into_iter(self) -> Self::IntoIter { self.nodes.into_iter() }
|
||||
}
|
||||
|
||||
impl<T> Path<T> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering},
|
||||
cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
|
||||
fmt, hash,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
@ -30,14 +30,10 @@ impl<T> PartialEq for Id<T> {
|
||||
fn eq(&self, other: &Self) -> bool { self.idx == other.idx && self.gen == other.gen }
|
||||
}
|
||||
impl<T> Ord for Id<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
(self.idx, self.gen).cmp(&(other.idx, other.gen))
|
||||
}
|
||||
fn cmp(&self, other: &Self) -> Ordering { (self.idx, self.gen).cmp(&(other.idx, other.gen)) }
|
||||
}
|
||||
impl<T> PartialOrd for Id<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
impl<T> fmt::Debug for Id<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@ -77,9 +73,7 @@ impl<T> Default for Store<T> {
|
||||
}
|
||||
|
||||
impl<T> Store<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
pub fn len(&self) -> usize { self.len }
|
||||
|
||||
pub fn contains(&self, id: Id<T>) -> bool {
|
||||
self.entries
|
||||
|
@ -364,7 +364,7 @@ image_ids! {
|
||||
mmap_site_cave_bg: "voxygen.element.map.cave_bg",
|
||||
mmap_site_cave_hover: "voxygen.element.map.cave_hover",
|
||||
mmap_site_cave: "voxygen.element.map.cave",
|
||||
mmap_site_excl: "voxygen.element.map.excl",
|
||||
mmap_site_excl: "voxygen.element.map.excl",
|
||||
|
||||
// Window Parts
|
||||
window_3: "voxygen.element.frames.window_3",
|
||||
|
@ -586,8 +586,10 @@ impl<'a> Widget for Map<'a> {
|
||||
if show_caves {
|
||||
site_btn.set(state.ids.mmap_site_icons[i], ui);
|
||||
}
|
||||
},
|
||||
_ => {site_btn.set(state.ids.mmap_site_icons[i], ui);},
|
||||
},
|
||||
_ => {
|
||||
site_btn.set(state.ids.mmap_site_icons[i], ui);
|
||||
},
|
||||
}
|
||||
|
||||
// Difficulty from 0-6
|
||||
|
@ -380,9 +380,15 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
|
||||
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
|
||||
let draw_delta = Vec3::new(1, 1, z_start);
|
||||
|
||||
let get_light = |_: &mut (), pos: Vec3<i32>| volume
|
||||
.get(range.min + pos)
|
||||
.map_or(1.0, |b| if b.is_opaque() { 0.0 } else { light(pos + range.min) });
|
||||
let get_light = |_: &mut (), pos: Vec3<i32>| {
|
||||
volume.get(range.min + pos).map_or(1.0, |b| {
|
||||
if b.is_opaque() {
|
||||
0.0
|
||||
} else {
|
||||
light(pos + range.min)
|
||||
}
|
||||
})
|
||||
};
|
||||
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
|
||||
let get_color =
|
||||
|_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
|
||||
|
@ -2,7 +2,8 @@ use core::{iter, mem};
|
||||
use hashbrown::HashMap;
|
||||
use num::traits::Float;
|
||||
pub use vek::{geom::repr_simd::*, mat::repr_simd::column_major::Mat4, ops::*, vec::repr_simd::*};
|
||||
//pub use vek::{geom::repr_c::*, mat::repr_c::column_major::Mat4, ops::*, vec::repr_c::*};
|
||||
//pub use vek::{geom::repr_c::*, mat::repr_c::column_major::Mat4, ops::*,
|
||||
// vec::repr_c::*};
|
||||
|
||||
pub fn aabb_to_points<T: Float>(bounds: Aabb<T>) -> [Vec3<T>; 8] {
|
||||
[
|
||||
|
@ -3,7 +3,7 @@ mod watcher;
|
||||
pub use self::watcher::BlocksOfInterest;
|
||||
|
||||
use crate::{
|
||||
mesh::{greedy::GreedyMesh, Meshable, terrain::SUNLIGHT},
|
||||
mesh::{greedy::GreedyMesh, terrain::SUNLIGHT, Meshable},
|
||||
render::{
|
||||
ColLightFmt, ColLightInfo, Consts, FluidPipeline, GlobalModel, Instances, Mesh, Model,
|
||||
RenderError, Renderer, ShadowPipeline, SpriteInstance, SpriteLocals, SpritePipeline,
|
||||
@ -524,22 +524,35 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
.map(|c| c.blocks_of_interest.lights.iter())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(move |(lpos, level)| (Vec3::<i32>::from(chunk_pos * TerrainChunk::RECT_SIZE.map(|e| e as i32)) + *lpos, level))
|
||||
.map(move |(lpos, level)| {
|
||||
(
|
||||
Vec3::<i32>::from(
|
||||
chunk_pos * TerrainChunk::RECT_SIZE.map(|e| e as i32),
|
||||
) + *lpos,
|
||||
level,
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.fold((Vec3::broadcast(0.001), 0.0, 0.0f32), |(bias, total, max), (lpos, level)| {
|
||||
let rpos = lpos.map(|e| e as f32 + 0.5) - wpos;
|
||||
let level = (*level as f32 - rpos.magnitude()).max(0.0) / SUNLIGHT as f32;
|
||||
(
|
||||
bias + rpos.try_normalized().unwrap_or_else(Vec3::zero) * level,
|
||||
total + level,
|
||||
max.max(level),
|
||||
)
|
||||
});
|
||||
.fold(
|
||||
(Vec3::broadcast(0.001), 0.0, 0.0f32),
|
||||
|(bias, total, max), (lpos, level)| {
|
||||
let rpos = lpos.map(|e| e as f32 + 0.5) - wpos;
|
||||
let level = (*level as f32 - rpos.magnitude()).max(0.0) / SUNLIGHT as f32;
|
||||
(
|
||||
bias + rpos.try_normalized().unwrap_or_else(Vec3::zero) * level,
|
||||
total + level,
|
||||
max.max(level),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
let bias_factor = bias.magnitude() * (1.0 - AMBIANCE) / total.max(0.001);
|
||||
|
||||
(bias.try_normalized().unwrap_or_else(Vec3::zero) * bias_factor.powf(0.5), self.glow_at_wpos(wpos.map(|e| e.floor() as i32)))
|
||||
(
|
||||
bias.try_normalized().unwrap_or_else(Vec3::zero) * bias_factor.powf(0.5),
|
||||
self.glow_at_wpos(wpos.map(|e| e.floor() as i32)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Maintain terrain data. To be called once per tick.
|
||||
@ -630,21 +643,19 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
// be meshed
|
||||
span!(guard, "Add chunks with modified blocks to mesh todo list");
|
||||
// TODO: would be useful if modified blocks were grouped by chunk
|
||||
for (&pos, &block) in scene_data
|
||||
.state
|
||||
.terrain_changes()
|
||||
.modified_blocks
|
||||
.iter()
|
||||
{
|
||||
// TODO: Be cleverer about this to avoid remeshing all neighbours. There are a few things that can create
|
||||
// an 'effect at a distance'. These are as follows:
|
||||
// - A glowing block is added or removed, thereby causing a lighting recalculation proportional to its glow
|
||||
// radius.
|
||||
// - An opaque block that was blocking sunlight from entering a cavity is removed (or added) thereby
|
||||
for (&pos, &block) in scene_data.state.terrain_changes().modified_blocks.iter() {
|
||||
// TODO: Be cleverer about this to avoid remeshing all neighbours. There are a
|
||||
// few things that can create an 'effect at a distance'. These are
|
||||
// as follows:
|
||||
// - A glowing block is added or removed, thereby causing a lighting
|
||||
// recalculation proportional to its glow radius.
|
||||
// - An opaque block that was blocking sunlight from entering a cavity is
|
||||
// removed (or added) thereby
|
||||
// changing the way that sunlight propagates into the cavity.
|
||||
//
|
||||
// We can and should be cleverer about this, but it's non-trivial. For now, just conservatively assume that
|
||||
// the lighting in all neighbouring chunks is invalidated. Thankfully, this doesn't need to happen often
|
||||
// We can and should be cleverer about this, but it's non-trivial. For now, just
|
||||
// conservatively assume that the lighting in all neighbouring
|
||||
// chunks is invalidated. Thankfully, this doesn't need to happen often
|
||||
// because block modification is unusual in Veloren.
|
||||
// let block_effect_radius = block.get_glow().unwrap_or(0).max(1);
|
||||
let block_effect_radius = crate::mesh::terrain::MAX_LIGHT_DIST;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::ops::Range;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use vek::*;
|
||||
use crate::util::math::close;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use std::ops::Range;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, IntoEnumIterator)]
|
||||
pub enum ForestKind {
|
||||
@ -33,7 +33,7 @@ impl ForestKind {
|
||||
ForestKind::Birch => 0.0..0.6,
|
||||
ForestKind::Mangrove => 0.65..1.3,
|
||||
ForestKind::Swamp => 0.5..1.1,
|
||||
_ => 0.0..0.0
|
||||
_ => 0.0..0.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,9 +76,11 @@ impl ForestKind {
|
||||
|
||||
pub fn proclivity(&self, env: &Environment) -> f32 {
|
||||
self.ideal_proclivity()
|
||||
* close(env.humid, self.humid_range())
|
||||
* close(env.temp, self.temp_range())
|
||||
* self.near_water_range().map_or(1.0, |near_water_range| close(env.near_water, near_water_range))
|
||||
* close(env.humid, self.humid_range())
|
||||
* close(env.temp, self.temp_range())
|
||||
* self.near_water_range().map_or(1.0, |near_water_range| {
|
||||
close(env.near_water, near_water_range)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
column::{ColumnGen, ColumnSample},
|
||||
util::{RandomField, Sampler, SmallCache, FastNoise},
|
||||
util::{FastNoise, RandomField, Sampler, SmallCache},
|
||||
IndexRef,
|
||||
};
|
||||
use common::terrain::{
|
||||
@ -121,14 +121,22 @@ impl<'a> BlockGen<'a> {
|
||||
|
||||
if stone_factor >= 0.5 {
|
||||
if wposf.z as f32 > height - cliff_offset.max(0.0) {
|
||||
if cliff_offset.max(0.0) > cliff_height - (FastNoise::new(37).get(wposf / Vec3::new(6.0, 6.0, 10.0)) * 0.5 + 0.5) * (height - grass_depth - wposf.z as f32).mul(0.25).clamped(0.0, 8.0) {
|
||||
if cliff_offset.max(0.0)
|
||||
> cliff_height
|
||||
- (FastNoise::new(37).get(wposf / Vec3::new(6.0, 6.0, 10.0)) * 0.5
|
||||
+ 0.5)
|
||||
* (height - grass_depth - wposf.z as f32)
|
||||
.mul(0.25)
|
||||
.clamped(0.0, 8.0)
|
||||
{
|
||||
Some(Block::empty())
|
||||
} else {
|
||||
let col = Lerp::lerp(
|
||||
col.map(|e| e as f32),
|
||||
col.map(|e| e as f32) * 0.7,
|
||||
(wposf.z as f32 - basement * 0.3).div(2.0).sin() * 0.5 + 0.5,
|
||||
).map(|e| e as u8);
|
||||
)
|
||||
.map(|e| e as u8);
|
||||
Some(Block::new(BlockKind::Rock, col))
|
||||
}
|
||||
} else {
|
||||
@ -198,7 +206,9 @@ pub struct ZCache<'a> {
|
||||
|
||||
impl<'a> ZCache<'a> {
|
||||
pub fn get_z_limits(&self) -> (f32, f32) {
|
||||
let min = self.sample.alt - (self.sample.chaos.min(1.0) * 16.0) - self.sample.cliff_offset.max(0.0);
|
||||
let min = self.sample.alt
|
||||
- (self.sample.chaos.min(1.0) * 16.0)
|
||||
- self.sample.cliff_offset.max(0.0);
|
||||
let min = min - 4.0;
|
||||
|
||||
let rocks = if self.sample.rock > 0.0 { 12.0 } else { 0.0 };
|
||||
|
@ -2,9 +2,9 @@ use crate::{
|
||||
block::ZCache,
|
||||
column::ColumnSample,
|
||||
index::IndexRef,
|
||||
land::Land,
|
||||
sim::{SimChunk, WorldSim},
|
||||
util::Grid,
|
||||
land::Land,
|
||||
};
|
||||
use common::{
|
||||
terrain::{Block, TerrainChunk, TerrainChunkSize},
|
||||
@ -48,9 +48,7 @@ impl<'a> CanvasInfo<'a> {
|
||||
|
||||
pub fn chunks(&self) -> &'a WorldSim { self.chunks }
|
||||
|
||||
pub fn land(&self) -> Land<'_> {
|
||||
Land::from_sim(self.chunks)
|
||||
}
|
||||
pub fn land(&self) -> Land<'_> { Land::from_sim(self.chunks) }
|
||||
}
|
||||
|
||||
pub struct Canvas<'a> {
|
||||
@ -66,7 +64,11 @@ impl<'a> Canvas<'a> {
|
||||
pub fn info(&mut self) -> CanvasInfo<'a> { self.info }
|
||||
|
||||
pub fn get(&mut self, pos: Vec3<i32>) -> Block {
|
||||
self.chunk.get(pos - self.wpos()).ok().copied().unwrap_or(Block::empty())
|
||||
self.chunk
|
||||
.get(pos - self.wpos())
|
||||
.ok()
|
||||
.copied()
|
||||
.unwrap_or(Block::empty())
|
||||
}
|
||||
|
||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
||||
|
@ -7,10 +7,9 @@ use crate::{
|
||||
config::CONFIG,
|
||||
sim::WorldSim,
|
||||
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite},
|
||||
util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS},
|
||||
site2,
|
||||
Index,
|
||||
Land,
|
||||
util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS},
|
||||
Index, Land,
|
||||
};
|
||||
use common::{
|
||||
astar::Astar,
|
||||
@ -216,9 +215,11 @@ impl Civs {
|
||||
SiteKind::Castle => {
|
||||
WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng))
|
||||
},
|
||||
SiteKind::Refactor => {
|
||||
WorldSite::refactor(site2::Site::generate(&Land::from_sim(&ctx.sim), &mut rng, wpos))
|
||||
},
|
||||
SiteKind::Refactor => WorldSite::refactor(site2::Site::generate(
|
||||
&Land::from_sim(&ctx.sim),
|
||||
&mut rng,
|
||||
wpos,
|
||||
)),
|
||||
});
|
||||
sim_site.site_tmp = Some(site);
|
||||
let site_ref = &index.sites[site];
|
||||
|
@ -272,11 +272,12 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
.rem_euclid(200.0)
|
||||
/ 64.0
|
||||
- 1.0;
|
||||
let cliff_scale = ((self.sim.gen_ctx.hill_nz.get(wposf.div(128.0).into_array()) as f32 * 1.5 + 0.75)
|
||||
+ self.sim.gen_ctx.hill_nz.get(wposf.div(48.0).into_array()) as f32 * 0.1)
|
||||
.clamped(0.0, 1.0).powf(2.0);
|
||||
let cliff_height =
|
||||
sim.get_interpolated(wpos, |chunk| chunk.cliff_height)? * cliff_scale;
|
||||
let cliff_scale =
|
||||
((self.sim.gen_ctx.hill_nz.get(wposf.div(128.0).into_array()) as f32 * 1.5 + 0.75)
|
||||
+ self.sim.gen_ctx.hill_nz.get(wposf.div(48.0).into_array()) as f32 * 0.1)
|
||||
.clamped(0.0, 1.0)
|
||||
.powf(2.0);
|
||||
let cliff_height = sim.get_interpolated(wpos, |chunk| chunk.cliff_height)? * cliff_scale;
|
||||
let cliff = if cliff_factor < 0.0 {
|
||||
cliff_factor.abs().powf(1.5)
|
||||
} else {
|
||||
|
@ -1,31 +1,31 @@
|
||||
use crate::sim;
|
||||
use common::{
|
||||
terrain::TerrainChunkSize,
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use common::{terrain::TerrainChunkSize, vol::RectVolSize};
|
||||
use vek::*;
|
||||
|
||||
/// A wrapper type that may contain a reference to a generated world. If not, default values will be provided.
|
||||
/// A wrapper type that may contain a reference to a generated world. If not,
|
||||
/// default values will be provided.
|
||||
pub struct Land<'a> {
|
||||
sim: Option<&'a sim::WorldSim>,
|
||||
}
|
||||
|
||||
impl<'a> Land<'a> {
|
||||
pub fn empty() -> Self {
|
||||
Self { sim: None }
|
||||
}
|
||||
pub fn empty() -> Self { Self { sim: None } }
|
||||
|
||||
pub fn from_sim(sim: &'a sim::WorldSim) -> Self {
|
||||
Self { sim: Some(sim) }
|
||||
}
|
||||
pub fn from_sim(sim: &'a sim::WorldSim) -> Self { Self { sim: Some(sim) } }
|
||||
|
||||
pub fn get_alt_approx(&self, wpos: Vec2<i32>) -> f32 {
|
||||
self.sim.and_then(|sim| sim.get_alt_approx(wpos)).unwrap_or(0.0)
|
||||
self.sim
|
||||
.and_then(|sim| sim.get_alt_approx(wpos))
|
||||
.unwrap_or(0.0)
|
||||
}
|
||||
|
||||
pub fn get_gradient_approx(&self, wpos: Vec2<i32>) -> f32 {
|
||||
self.sim
|
||||
.and_then(|sim| sim.get_gradient_approx(wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e.div_euclid(sz as i32))))
|
||||
.and_then(|sim| {
|
||||
sim.get_gradient_approx(
|
||||
wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e.div_euclid(sz as i32)),
|
||||
)
|
||||
})
|
||||
.unwrap_or(0.0)
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,10 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
.mul(45.0) as i32;
|
||||
|
||||
// Generate stalagtites if there's something for them to hold on to
|
||||
if canvas.get(Vec3::new(wpos2d.x, wpos2d.y, cave_roof)).is_filled() {
|
||||
if canvas
|
||||
.get(Vec3::new(wpos2d.x, wpos2d.y, cave_roof))
|
||||
.is_filled()
|
||||
{
|
||||
for z in cave_roof - stalagtites..cave_roof {
|
||||
canvas.set(
|
||||
Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||
|
@ -577,11 +577,15 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
// surface
|
||||
if let Some(solid_end) = (-4..8)
|
||||
.find(|z| {
|
||||
canvas.get(Vec3::new(wpos2d.x, wpos2d.y, alt + z)).is_solid()
|
||||
canvas
|
||||
.get(Vec3::new(wpos2d.x, wpos2d.y, alt + z))
|
||||
.is_solid()
|
||||
})
|
||||
.and_then(|solid_start| {
|
||||
(1..8).map(|z| solid_start + z).find(|z| {
|
||||
!canvas.get(Vec3::new(wpos2d.x, wpos2d.y, alt + z)).is_solid()
|
||||
!canvas
|
||||
.get(Vec3::new(wpos2d.x, wpos2d.y, alt + z))
|
||||
.is_solid()
|
||||
})
|
||||
})
|
||||
{
|
||||
|
@ -60,7 +60,14 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||
let trees = info.chunks().get_near_trees(wpos2d);
|
||||
|
||||
for TreeAttr { pos, seed, scale, forest_kind, inhabited } in trees {
|
||||
for TreeAttr {
|
||||
pos,
|
||||
seed,
|
||||
scale,
|
||||
forest_kind,
|
||||
inhabited,
|
||||
} in trees
|
||||
{
|
||||
let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| {
|
||||
let col = ColumnGen::new(info.chunks()).get((pos, info.index()))?;
|
||||
|
||||
@ -127,7 +134,11 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
ForestKind::Giant => {
|
||||
break 'model TreeModel::Procedural(
|
||||
ProceduralTree::generate(
|
||||
TreeConfig::giant(&mut RandomPerm::new(seed), scale, inhabited),
|
||||
TreeConfig::giant(
|
||||
&mut RandomPerm::new(seed),
|
||||
scale,
|
||||
inhabited,
|
||||
),
|
||||
&mut RandomPerm::new(seed),
|
||||
),
|
||||
StructureBlock::TemperateLeaves,
|
||||
@ -184,7 +195,9 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
TreeModel::Structure(s) => s.get(model_pos).ok().copied(),
|
||||
TreeModel::Procedural(t, leaf_block) => Some(
|
||||
match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
|
||||
(_, _, true, _) => StructureBlock::Block(BlockKind::Wood, Rgb::new(110, 68, 22)),
|
||||
(_, _, true, _) => {
|
||||
StructureBlock::Block(BlockKind::Wood, Rgb::new(110, 68, 22))
|
||||
},
|
||||
(_, _, _, true) => StructureBlock::None,
|
||||
(true, _, _, _) => StructureBlock::Log,
|
||||
(_, true, _, _) => *leaf_block,
|
||||
@ -204,9 +217,14 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
)
|
||||
.map(|block| {
|
||||
// Add lights to the tree
|
||||
if inhabited && last_block.is_air() && block.kind() == BlockKind::Wood && dynamic_rng.gen_range(0..256) == 0 {
|
||||
if inhabited
|
||||
&& last_block.is_air()
|
||||
&& block.kind() == BlockKind::Wood
|
||||
&& dynamic_rng.gen_range(0..256) == 0
|
||||
{
|
||||
canvas.set(wpos + Vec3::unit_z(), Block::air(SpriteKind::Lantern));
|
||||
// Add a snow covering to the block above under certain circumstances
|
||||
// Add a snow covering to the block above under certain
|
||||
// circumstances
|
||||
} else if col.snow_cover
|
||||
&& ((block.kind() == BlockKind::Leaves && is_leaf_top)
|
||||
|| (is_top && block.is_filled()))
|
||||
@ -423,8 +441,7 @@ impl ProceduralTree {
|
||||
|
||||
const PHI: f32 = 0.618;
|
||||
const RAD_PER_BRANCH: f32 = f32::consts::TAU * PHI;
|
||||
let screw = (screw_shift + dist * splits as f32 * RAD_PER_BRANCH).sin()
|
||||
* x_axis
|
||||
let screw = (screw_shift + dist * splits as f32 * RAD_PER_BRANCH).sin() * x_axis
|
||||
+ (screw_shift + dist * splits as f32 * RAD_PER_BRANCH).cos() * y_axis;
|
||||
|
||||
// Choose a point close to the branch to act as the target direction for the
|
||||
@ -488,7 +505,12 @@ impl ProceduralTree {
|
||||
pub fn get_bounds(&self) -> Aabb<f32> { self.branches[self.trunk_idx].aabb }
|
||||
|
||||
// Recursively search for branches or leaves by walking the tree's branch graph.
|
||||
fn is_branch_or_leaves_at_inner(&self, pos: Vec3<f32>, parent: &Branch, branch_idx: usize) -> (bool, bool, bool, bool) {
|
||||
fn is_branch_or_leaves_at_inner(
|
||||
&self,
|
||||
pos: Vec3<f32>,
|
||||
parent: &Branch,
|
||||
branch_idx: usize,
|
||||
) -> (bool, bool, bool, bool) {
|
||||
let branch = &self.branches[branch_idx];
|
||||
// Always probe the sibling branch, since our AABB doesn't include its bounds
|
||||
// (it's not one of our children)
|
||||
@ -506,7 +528,8 @@ impl ProceduralTree {
|
||||
let siblings = branch_or_leaves | Vec4::from(this);
|
||||
|
||||
// Probe the children of this branch
|
||||
let children = branch.child_idx
|
||||
let children = branch
|
||||
.child_idx
|
||||
.map(|idx| Vec4::<bool>::from(self.is_branch_or_leaves_at_inner(pos, branch, idx)))
|
||||
.unwrap_or_default();
|
||||
|
||||
@ -521,8 +544,9 @@ impl ProceduralTree {
|
||||
/// position in the tree.
|
||||
#[inline(always)]
|
||||
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool, bool, bool) {
|
||||
let (log, leaf, platform, air) = self.is_branch_or_leaves_at_inner(pos, &self.branches[self.trunk_idx], self.trunk_idx);
|
||||
(log /*& !air*/, leaf & !air, platform & !air, air)
|
||||
let (log, leaf, platform, air) =
|
||||
self.is_branch_or_leaves_at_inner(pos, &self.branches[self.trunk_idx], self.trunk_idx);
|
||||
(log /* & !air */, leaf & !air, platform & !air, air)
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,7 +571,11 @@ struct Branch {
|
||||
impl Branch {
|
||||
/// Determine whether there are either branches or leaves at the given
|
||||
/// position in the branch.
|
||||
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>, parent: &Branch) -> ((bool, bool, bool, bool), f32) {
|
||||
pub fn is_branch_or_leaves_at(
|
||||
&self,
|
||||
pos: Vec3<f32>,
|
||||
parent: &Branch,
|
||||
) -> ((bool, bool, bool, bool), f32) {
|
||||
// fn finvsqrt(x: f32) -> f32 {
|
||||
// let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
|
||||
// y * (1.5 - ( x * 0.5 * y * y ))
|
||||
@ -588,20 +616,32 @@ impl Branch {
|
||||
let stair_thickness = 2.0;
|
||||
let stair_space = 5.0;
|
||||
if self.has_stairs {
|
||||
let (platform, air) = if pos.z >= self.line.start.z.min(self.line.end.z) - 1.0 && pos.z <= self.line.start.z.max(self.line.end.z) + stair_thickness + stair_space && d2 < (wood_radius + stair_width).powi(2) {
|
||||
let (platform, air) = if pos.z >= self.line.start.z.min(self.line.end.z) - 1.0
|
||||
&& pos.z
|
||||
<= self.line.start.z.max(self.line.end.z) + stair_thickness + stair_space
|
||||
&& d2 < (wood_radius + stair_width).powi(2)
|
||||
{
|
||||
let rpos = pos.xy() - p;
|
||||
let stretch = 32.0;
|
||||
let stair_section = ((rpos.x as f32).atan2(rpos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z).rem_euclid(stretch);
|
||||
(stair_section < stair_thickness, stair_section >= stair_thickness && stair_section < stair_thickness + stair_space) // Stairs
|
||||
let stair_section =
|
||||
((rpos.x as f32).atan2(rpos.y as f32) / (f32::consts::PI * 2.0) * stretch
|
||||
+ pos.z)
|
||||
.rem_euclid(stretch);
|
||||
(
|
||||
stair_section < stair_thickness,
|
||||
stair_section >= stair_thickness
|
||||
&& stair_section < stair_thickness + stair_space,
|
||||
) // Stairs
|
||||
} else {
|
||||
(false, false)
|
||||
};
|
||||
|
||||
let platform = platform || (self.has_stairs
|
||||
&& self.wood_radius > 4.0
|
||||
&& !air
|
||||
&& d2 < (wood_radius + 10.0).powi(2)
|
||||
&& pos.z % 48.0 < stair_thickness);
|
||||
let platform = platform
|
||||
|| (self.has_stairs
|
||||
&& self.wood_radius > 4.0
|
||||
&& !air
|
||||
&& d2 < (wood_radius + 10.0).powi(2)
|
||||
&& pos.z % 48.0 < stair_thickness);
|
||||
|
||||
(false, false, platform, air)
|
||||
} else {
|
||||
|
@ -166,7 +166,11 @@ pub fn sample_pos(
|
||||
});
|
||||
|
||||
let downhill_wpos = downhill.unwrap_or(wpos + TerrainChunkSize::RECT_SIZE.map(|e| e as i32));
|
||||
let alt = if is_basement { basement } else { column_rgb_alt.map_or(alt, |(_, alt)| alt) };
|
||||
let alt = if is_basement {
|
||||
basement
|
||||
} else {
|
||||
column_rgb_alt.map_or(alt, |(_, alt)| alt)
|
||||
};
|
||||
|
||||
let true_water_alt = (alt.max(water_alt) as f64 - focus.z) / gain as f64;
|
||||
let true_alt = (alt as f64 - focus.z) / gain as f64;
|
||||
|
@ -29,22 +29,22 @@ use crate::{
|
||||
column::ColumnGen,
|
||||
site::Site,
|
||||
util::{
|
||||
seed_expan, FastNoise, FastNoise2d, RandomField, RandomPerm, Sampler, StructureGen2d, LOCALITY,
|
||||
NEIGHBORS, CARDINALS, DHashSet,
|
||||
seed_expan, DHashSet, FastNoise, FastNoise2d, RandomField, RandomPerm, Sampler,
|
||||
StructureGen2d, CARDINALS, LOCALITY, NEIGHBORS,
|
||||
},
|
||||
IndexRef, CONFIG,
|
||||
};
|
||||
use common::{
|
||||
assets::{self, AssetExt},
|
||||
grid::Grid,
|
||||
lottery::Lottery,
|
||||
spiral::Spiral2d,
|
||||
store::Id,
|
||||
terrain::{
|
||||
map::MapConfig, uniform_idx_as_vec2, vec2_as_uniform_idx, BiomeKind, MapSizeLg,
|
||||
TerrainChunkSize,
|
||||
},
|
||||
vol::RectVolSize,
|
||||
lottery::Lottery,
|
||||
spiral::Spiral2d,
|
||||
};
|
||||
use common_net::msg::WorldMapMsg;
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
@ -1535,7 +1535,10 @@ impl WorldSim {
|
||||
pos += CARDINALS
|
||||
.iter()
|
||||
.copied()
|
||||
.max_by_key(|rpos| self.get_gradient_approx(pos + rpos).map_or(0, |g| (g * 1000.0) as i32))
|
||||
.max_by_key(|rpos| {
|
||||
self.get_gradient_approx(pos + rpos)
|
||||
.map_or(0, |g| (g * 1000.0) as i32)
|
||||
})
|
||||
.unwrap(); // Can't fail
|
||||
} else {
|
||||
break;
|
||||
@ -1563,8 +1566,8 @@ impl WorldSim {
|
||||
// self.get_mut(locs[2].0).unwrap().cliff.0.neighbors |=
|
||||
// 1 << ((to_next_idx as u8 + 4) % 8);
|
||||
|
||||
// self.get_mut(locs[1].0).unwrap().cliff.0.offset = Vec2::new(rng.gen_range(-16..17), rng.gen_range(-16..17));
|
||||
// }
|
||||
// self.get_mut(locs[1].0).unwrap().cliff.0.offset =
|
||||
// Vec2::new(rng.gen_range(-16..17), rng.gen_range(-16..17)); }
|
||||
|
||||
for cliff in cliffs {
|
||||
let alt = self.get(cliff).map_or(0.0, |c| c.alt);
|
||||
@ -2076,21 +2079,30 @@ impl WorldSim {
|
||||
let env = Environment {
|
||||
humid: chunk.humidity,
|
||||
temp: chunk.temp,
|
||||
near_water: if chunk.river.is_lake() || chunk.river.near_river() { 1.0 } else { 0.0 },
|
||||
near_water: if chunk.river.is_lake() || chunk.river.near_river() {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
};
|
||||
Some(TreeAttr {
|
||||
pos,
|
||||
seed,
|
||||
scale: 1.0,
|
||||
forest_kind: *Lottery::from(ForestKind::into_enum_iter()
|
||||
.enumerate()
|
||||
.map(|(i, fk)| {
|
||||
const CLUSTER_SIZE: f64 = 48.0;
|
||||
let nz = (FastNoise2d::new(i as u32 * 37).get(pos.map(|e| e as f64) / CLUSTER_SIZE) + 1.0) / 2.0;
|
||||
(fk.proclivity(&env) * nz, fk)
|
||||
})
|
||||
.collect::<Vec<_>>())
|
||||
.choose_seeded(seed),
|
||||
forest_kind: *Lottery::from(
|
||||
ForestKind::into_enum_iter()
|
||||
.enumerate()
|
||||
.map(|(i, fk)| {
|
||||
const CLUSTER_SIZE: f64 = 48.0;
|
||||
let nz = (FastNoise2d::new(i as u32 * 37)
|
||||
.get(pos.map(|e| e as f64) / CLUSTER_SIZE)
|
||||
+ 1.0)
|
||||
/ 2.0;
|
||||
(fk.proclivity(&env) * nz, fk)
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.choose_seeded(seed),
|
||||
inhabited: false,
|
||||
})
|
||||
});
|
||||
@ -2344,7 +2356,11 @@ impl SimChunk {
|
||||
let env = Environment {
|
||||
humid: humidity,
|
||||
temp,
|
||||
near_water: if river.is_lake() || river.near_river() { 1.0 } else { 0.0 },
|
||||
near_water: if river.is_lake() || river.near_river() {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
},
|
||||
};
|
||||
|
||||
ForestKind::into_enum_iter()
|
||||
@ -2393,7 +2409,5 @@ impl SimChunk {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn near_cliffs(&self) -> bool {
|
||||
self.cliff_height > 0.0
|
||||
}
|
||||
pub fn near_cliffs(&self) -> bool { self.cliff_height > 0.0 }
|
||||
}
|
||||
|
@ -11,12 +11,7 @@ pub use self::{
|
||||
settlement::Settlement,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
column::ColumnSample,
|
||||
IndexRef,
|
||||
site2,
|
||||
Canvas,
|
||||
};
|
||||
use crate::{column::ColumnSample, site2, Canvas, IndexRef};
|
||||
use common::{
|
||||
generation::ChunkSupplement,
|
||||
terrain::Block,
|
||||
@ -118,11 +113,7 @@ impl Site {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_to<'a>(
|
||||
&'a self,
|
||||
canvas: &mut Canvas,
|
||||
dynamic_rng: &mut impl Rng,
|
||||
) {
|
||||
pub fn apply_to<'a>(&'a self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
let info = canvas.info();
|
||||
let get_col = |wpos| info.col(wpos + info.wpos);
|
||||
match &self.kind {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use common::{
|
||||
terrain::Block,
|
||||
store::{Id, Store},
|
||||
terrain::Block,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
@ -26,9 +26,11 @@ pub struct Fill {
|
||||
impl Fill {
|
||||
fn contains_at(&self, tree: &Store<Primitive>, prim: Id<Primitive>, pos: Vec3<i32>) -> bool {
|
||||
// Custom closure because vek's impl of `contains_point` is inclusive :(
|
||||
let aabb_contains = |aabb: Aabb<i32>, pos: Vec3<i32>| (aabb.min.x..aabb.max.x).contains(&pos.x)
|
||||
&& (aabb.min.y..aabb.max.y).contains(&pos.y)
|
||||
&& (aabb.min.z..aabb.max.z).contains(&pos.z);
|
||||
let aabb_contains = |aabb: Aabb<i32>, pos: Vec3<i32>| {
|
||||
(aabb.min.x..aabb.max.x).contains(&pos.x)
|
||||
&& (aabb.min.y..aabb.max.y).contains(&pos.y)
|
||||
&& (aabb.min.z..aabb.max.z).contains(&pos.z)
|
||||
};
|
||||
|
||||
match &tree[prim] {
|
||||
Primitive::Empty => false,
|
||||
@ -36,15 +38,28 @@ impl Fill {
|
||||
Primitive::Aabb(aabb) => aabb_contains(*aabb, pos),
|
||||
Primitive::Pyramid { aabb, inset } => {
|
||||
let inset = (*inset).max(aabb.size().reduce_min());
|
||||
let inner = Aabr { min: aabb.min.xy() - 1 + inset, max: aabb.max.xy() - inset };
|
||||
aabb_contains(*aabb, pos) && (inner.projected_point(pos.xy()) - pos.xy())
|
||||
.map(|e| e.abs())
|
||||
.reduce_max() as f32 / (inset as f32) < 1.0 - ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
|
||||
let inner = Aabr {
|
||||
min: aabb.min.xy() - 1 + inset,
|
||||
max: aabb.max.xy() - inset,
|
||||
};
|
||||
aabb_contains(*aabb, pos)
|
||||
&& (inner.projected_point(pos.xy()) - pos.xy())
|
||||
.map(|e| e.abs())
|
||||
.reduce_max() as f32
|
||||
/ (inset as f32)
|
||||
< 1.0
|
||||
- ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
|
||||
},
|
||||
|
||||
Primitive::And(a, b) => self.contains_at(tree, *a, pos) && self.contains_at(tree, *b, pos),
|
||||
Primitive::Or(a, b) => self.contains_at(tree, *a, pos) || self.contains_at(tree, *b, pos),
|
||||
Primitive::Xor(a, b) => self.contains_at(tree, *a, pos) ^ self.contains_at(tree, *b, pos),
|
||||
Primitive::And(a, b) => {
|
||||
self.contains_at(tree, *a, pos) && self.contains_at(tree, *b, pos)
|
||||
},
|
||||
Primitive::Or(a, b) => {
|
||||
self.contains_at(tree, *a, pos) || self.contains_at(tree, *b, pos)
|
||||
},
|
||||
Primitive::Xor(a, b) => {
|
||||
self.contains_at(tree, *a, pos) ^ self.contains_at(tree, *b, pos)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,14 +80,22 @@ impl Fill {
|
||||
Primitive::Empty => return None,
|
||||
Primitive::Aabb(aabb) => *aabb,
|
||||
Primitive::Pyramid { aabb, .. } => *aabb,
|
||||
Primitive::And(a, b) => or_zip_with(self.get_bounds_inner(tree, *a), self.get_bounds_inner(tree, *b), |a, b| a.intersection(b))?,
|
||||
Primitive::Or(a, b) | Primitive::Xor(a, b) =>
|
||||
or_zip_with(self.get_bounds_inner(tree, *a), self.get_bounds_inner(tree, *b), |a, b| a.union(b))?,
|
||||
Primitive::And(a, b) => or_zip_with(
|
||||
self.get_bounds_inner(tree, *a),
|
||||
self.get_bounds_inner(tree, *b),
|
||||
|a, b| a.intersection(b),
|
||||
)?,
|
||||
Primitive::Or(a, b) | Primitive::Xor(a, b) => or_zip_with(
|
||||
self.get_bounds_inner(tree, *a),
|
||||
self.get_bounds_inner(tree, *b),
|
||||
|a, b| a.union(b),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_bounds(&self, tree: &Store<Primitive>) -> Aabb<i32> {
|
||||
self.get_bounds_inner(tree, self.prim).unwrap_or_else(|| Aabb::new_empty(Vec3::zero()))
|
||||
self.get_bounds_inner(tree, self.prim)
|
||||
.unwrap_or_else(|| Aabb::new_empty(Vec3::zero()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +105,8 @@ pub trait Structure {
|
||||
site: &Site,
|
||||
prim: F,
|
||||
fill: G,
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
|
||||
// Generate a primitive tree and fills for this structure
|
||||
fn render_collect(&self, site: &Site) -> (Store<Primitive>, Vec<Fill>) {
|
||||
|
@ -3,38 +3,37 @@ mod plot;
|
||||
mod tile;
|
||||
|
||||
use self::{
|
||||
gen::{Fill, Primitive, Structure},
|
||||
plot::{Plot, PlotKind},
|
||||
tile::{TileGrid, Tile, TileKind, HazardKind, TILE_SIZE},
|
||||
gen::{Primitive, Fill, Structure},
|
||||
tile::{HazardKind, Tile, TileGrid, TileKind, TILE_SIZE},
|
||||
};
|
||||
use crate::{
|
||||
site::SpawnRules,
|
||||
util::{Grid, attempt, DHashSet, CARDINALS, SQUARE_4, SQUARE_9, LOCALITY},
|
||||
Canvas,
|
||||
Land,
|
||||
util::{attempt, DHashSet, Grid, CARDINALS, LOCALITY, SQUARE_4, SQUARE_9},
|
||||
Canvas, Land,
|
||||
};
|
||||
use common::{
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
store::{Id, Store},
|
||||
astar::Astar,
|
||||
lottery::Lottery,
|
||||
spiral::Spiral2d,
|
||||
store::{Id, Store},
|
||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||
vol::RectVolSize,
|
||||
};
|
||||
use hashbrown::hash_map::DefaultHashBuilder;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use vek::*;
|
||||
use std::ops::Range;
|
||||
use vek::*;
|
||||
|
||||
/// Seed a new RNG from an old RNG, thereby making the old RNG indepedent of changing use of the new RNG. The practical
|
||||
/// effect of this is to reduce the extent to which changes to child generation algorithm produce a 'butterfly effect'
|
||||
/// on their parent generators, meaning that generators will be less likely to produce entirely different outcomes if
|
||||
/// some detail of a generation algorithm changes slightly. This is generally good and makes worldgen code easier to
|
||||
/// maintain and less liable to breaking changes.
|
||||
fn reseed(rng: &mut impl Rng) -> impl Rng {
|
||||
ChaChaRng::from_seed(rng.gen::<[u8; 32]>())
|
||||
}
|
||||
/// Seed a new RNG from an old RNG, thereby making the old RNG indepedent of
|
||||
/// changing use of the new RNG. The practical effect of this is to reduce the
|
||||
/// extent to which changes to child generation algorithm produce a 'butterfly
|
||||
/// effect' on their parent generators, meaning that generators will be less
|
||||
/// likely to produce entirely different outcomes if some detail of a generation
|
||||
/// algorithm changes slightly. This is generally good and makes worldgen code
|
||||
/// easier to maintain and less liable to breaking changes.
|
||||
fn reseed(rng: &mut impl Rng) -> impl Rng { ChaChaRng::from_seed(rng.gen::<[u8; 32]>()) }
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Site {
|
||||
@ -47,15 +46,23 @@ pub struct Site {
|
||||
|
||||
impl Site {
|
||||
pub fn radius(&self) -> f32 {
|
||||
((self.tiles.bounds.min.map(|e| e.abs()).reduce_max()
|
||||
.max(self.tiles.bounds.max.map(|e| e.abs()).reduce_max()) + 1) * tile::TILE_SIZE as i32) as f32
|
||||
((self
|
||||
.tiles
|
||||
.bounds
|
||||
.min
|
||||
.map(|e| e.abs())
|
||||
.reduce_max()
|
||||
.max(self.tiles.bounds.max.map(|e| e.abs()).reduce_max())
|
||||
+ 1)
|
||||
* tile::TILE_SIZE as i32) as f32
|
||||
}
|
||||
|
||||
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
|
||||
SpawnRules {
|
||||
trees: SQUARE_9
|
||||
.iter()
|
||||
.all(|&rpos| self.wpos_tile(wpos + rpos * tile::TILE_SIZE as i32).is_empty()),
|
||||
trees: SQUARE_9.iter().all(|&rpos| {
|
||||
self.wpos_tile(wpos + rpos * tile::TILE_SIZE as i32)
|
||||
.is_empty()
|
||||
}),
|
||||
..SpawnRules::default()
|
||||
}
|
||||
}
|
||||
@ -82,7 +89,14 @@ impl Site {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_road(&mut self, land: &Land, rng: &mut impl Rng, a: Vec2<i32>, b: Vec2<i32>, w: u16) -> Option<Id<Plot>> {
|
||||
pub fn create_road(
|
||||
&mut self,
|
||||
land: &Land,
|
||||
rng: &mut impl Rng,
|
||||
a: Vec2<i32>,
|
||||
b: Vec2<i32>,
|
||||
w: u16,
|
||||
) -> Option<Id<Plot>> {
|
||||
const MAX_ITERS: usize = 4096;
|
||||
let range = -(w as i32) / 2..w as i32 - w as i32 / 2;
|
||||
let heuristic = |tile: &Vec2<i32>| {
|
||||
@ -95,17 +109,22 @@ impl Site {
|
||||
}
|
||||
(tile.distance_squared(b) as f32).sqrt()
|
||||
};
|
||||
let path = Astar::new(MAX_ITERS, a, &heuristic, DefaultHashBuilder::default()).poll(
|
||||
MAX_ITERS,
|
||||
&heuristic,
|
||||
|tile| { let tile = *tile; CARDINALS.iter().map(move |dir| tile + *dir) },
|
||||
|a, b| {
|
||||
let alt_a = land.get_alt_approx(self.tile_center_wpos(*a));
|
||||
let alt_b = land.get_alt_approx(self.tile_center_wpos(*b));
|
||||
(alt_a - alt_b).abs() / TILE_SIZE as f32
|
||||
},
|
||||
|tile| *tile == b,
|
||||
).into_path()?;
|
||||
let path = Astar::new(MAX_ITERS, a, &heuristic, DefaultHashBuilder::default())
|
||||
.poll(
|
||||
MAX_ITERS,
|
||||
&heuristic,
|
||||
|tile| {
|
||||
let tile = *tile;
|
||||
CARDINALS.iter().map(move |dir| tile + *dir)
|
||||
},
|
||||
|a, b| {
|
||||
let alt_a = land.get_alt_approx(self.tile_center_wpos(*a));
|
||||
let alt_b = land.get_alt_approx(self.tile_center_wpos(*b));
|
||||
(alt_a - alt_b).abs() / TILE_SIZE as f32
|
||||
},
|
||||
|tile| *tile == b,
|
||||
)
|
||||
.into_path()?;
|
||||
|
||||
let plot = self.create_plot(Plot {
|
||||
kind: PlotKind::Road(path.clone()),
|
||||
@ -134,24 +153,41 @@ impl Site {
|
||||
Some(plot)
|
||||
}
|
||||
|
||||
pub fn find_aabr(&mut self, search_pos: Vec2<i32>, area_range: Range<u32>, min_dims: Extent2<u32>) -> Option<(Aabr<i32>, Vec2<i32>)> {
|
||||
self.tiles.find_near(
|
||||
search_pos,
|
||||
|center, _| self.tiles.grow_aabr(center, area_range.clone(), min_dims)
|
||||
pub fn find_aabr(
|
||||
&mut self,
|
||||
search_pos: Vec2<i32>,
|
||||
area_range: Range<u32>,
|
||||
min_dims: Extent2<u32>,
|
||||
) -> Option<(Aabr<i32>, Vec2<i32>)> {
|
||||
self.tiles.find_near(search_pos, |center, _| {
|
||||
self.tiles
|
||||
.grow_aabr(center, area_range.clone(), min_dims)
|
||||
.ok()
|
||||
.filter(|aabr| {
|
||||
(aabr.min.x..aabr.max.x).any(|x| self.tiles.get(Vec2::new(x, aabr.min.y - 1)).is_road())
|
||||
|| (aabr.min.x..aabr.max.x).any(|x| self.tiles.get(Vec2::new(x, aabr.max.y)).is_road())
|
||||
|| (aabr.min.y..aabr.max.y).any(|y| self.tiles.get(Vec2::new(aabr.min.x - 1, y)).is_road())
|
||||
|| (aabr.min.y..aabr.max.y).any(|y| self.tiles.get(Vec2::new(aabr.max.x, y)).is_road())
|
||||
}),
|
||||
)
|
||||
(aabr.min.x..aabr.max.x)
|
||||
.any(|x| self.tiles.get(Vec2::new(x, aabr.min.y - 1)).is_road())
|
||||
|| (aabr.min.x..aabr.max.x)
|
||||
.any(|x| self.tiles.get(Vec2::new(x, aabr.max.y)).is_road())
|
||||
|| (aabr.min.y..aabr.max.y)
|
||||
.any(|y| self.tiles.get(Vec2::new(aabr.min.x - 1, y)).is_road())
|
||||
|| (aabr.min.y..aabr.max.y)
|
||||
.any(|y| self.tiles.get(Vec2::new(aabr.max.x, y)).is_road())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_roadside_aabr(&mut self, rng: &mut impl Rng, area_range: Range<u32>, min_dims: Extent2<u32>) -> Option<(Aabr<i32>, Vec2<i32>)> {
|
||||
let dir = Vec2::<f32>::zero().map(|_| rng.gen_range(-1.0..1.0)).normalized();
|
||||
pub fn find_roadside_aabr(
|
||||
&mut self,
|
||||
rng: &mut impl Rng,
|
||||
area_range: Range<u32>,
|
||||
min_dims: Extent2<u32>,
|
||||
) -> Option<(Aabr<i32>, Vec2<i32>)> {
|
||||
let dir = Vec2::<f32>::zero()
|
||||
.map(|_| rng.gen_range(-1.0..1.0))
|
||||
.normalized();
|
||||
let search_pos = if rng.gen() {
|
||||
self.plot(*self.plazas.choose(rng)?).root_tile + (dir * 4.0).map(|e: f32| e.round() as i32)
|
||||
self.plot(*self.plazas.choose(rng)?).root_tile
|
||||
+ (dir * 4.0).map(|e: f32| e.round() as i32)
|
||||
} else {
|
||||
if let PlotKind::Road(path) = &self.plot(*self.roads.choose(rng)?).kind {
|
||||
*path.nodes().choose(rng)? + (dir * 1.0).map(|e: f32| e.round() as i32)
|
||||
@ -167,17 +203,27 @@ impl Site {
|
||||
let pos = attempt(32, || {
|
||||
self.plazas
|
||||
.choose(rng)
|
||||
.map(|&p| self.plot(p).root_tile + (Vec2::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0)).normalized() * 24.0).map(|e| e as i32))
|
||||
.map(|&p| {
|
||||
self.plot(p).root_tile
|
||||
+ (Vec2::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0))
|
||||
.normalized()
|
||||
* 24.0)
|
||||
.map(|e| e as i32)
|
||||
})
|
||||
.filter(|tile| !self.tiles.get(*tile).is_obstacle())
|
||||
.filter(|&tile| self
|
||||
.plazas
|
||||
.iter()
|
||||
.all(|&p| self.plot(p).root_tile.distance_squared(tile) > 20i32.pow(2))
|
||||
&& rng.gen_range(0..48) > tile.map(|e| e.abs()).reduce_max())
|
||||
.filter(|&tile| {
|
||||
self.plazas
|
||||
.iter()
|
||||
.all(|&p| self.plot(p).root_tile.distance_squared(tile) > 20i32.pow(2))
|
||||
&& rng.gen_range(0..48) > tile.map(|e| e.abs()).reduce_max()
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(Vec2::zero);
|
||||
.unwrap_or_else(Vec2::zero);
|
||||
|
||||
let aabr = Aabr { min: pos + Vec2::broadcast(-3), max: pos + Vec2::broadcast(4) };
|
||||
let aabr = Aabr {
|
||||
min: pos + Vec2::broadcast(-3),
|
||||
max: pos + Vec2::broadcast(4),
|
||||
};
|
||||
let plaza = self.create_plot(Plot {
|
||||
kind: PlotKind::Plaza,
|
||||
root_tile: pos,
|
||||
@ -193,7 +239,8 @@ impl Site {
|
||||
let mut already_pathed = vec![plaza];
|
||||
// One major, one minor road
|
||||
for width in (1..=2).rev() {
|
||||
if let Some(&p) = self.plazas
|
||||
if let Some(&p) = self
|
||||
.plazas
|
||||
.iter()
|
||||
.filter(|p| !already_pathed.contains(p))
|
||||
.min_by_key(|&&p| self.plot(p).root_tile.distance_squared(pos))
|
||||
@ -217,7 +264,9 @@ impl Site {
|
||||
if let Some(kind) = wpos_is_hazard(land, self.tile_wpos(tile)) {
|
||||
for &rpos in &SQUARE_4 {
|
||||
// `get_mut` doesn't increase generation bounds
|
||||
self.tiles.get_mut(tile - rpos - 1).map(|tile| tile.kind = TileKind::Hazard(kind));
|
||||
self.tiles
|
||||
.get_mut(tile - rpos - 1)
|
||||
.map(|tile| tile.kind = TileKind::Hazard(kind));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -235,12 +284,7 @@ impl Site {
|
||||
|
||||
site.make_plaza(land, &mut rng);
|
||||
|
||||
let build_chance = Lottery::from(vec![
|
||||
(64.0, 1),
|
||||
(5.0, 2),
|
||||
(20.0, 3),
|
||||
(0.75, 4),
|
||||
]);
|
||||
let build_chance = Lottery::from(vec![(64.0, 1), (5.0, 2), (20.0, 3), (0.75, 4)]);
|
||||
|
||||
let mut castles = 0;
|
||||
|
||||
@ -249,9 +293,21 @@ impl Site {
|
||||
// House
|
||||
1 => {
|
||||
let size = (2.0 + rng.gen::<f32>().powf(8.0) * 3.0).round() as u32;
|
||||
if let Some((aabr, door_tile)) = attempt(32, || site.find_roadside_aabr(&mut rng, 4..(size + 1).pow(2), Extent2::broadcast(size))) {
|
||||
if let Some((aabr, door_tile)) = attempt(32, || {
|
||||
site.find_roadside_aabr(
|
||||
&mut rng,
|
||||
4..(size + 1).pow(2),
|
||||
Extent2::broadcast(size),
|
||||
)
|
||||
}) {
|
||||
let plot = site.create_plot(Plot {
|
||||
kind: PlotKind::House(plot::House::generate(land, &mut reseed(&mut rng), &site, door_tile, aabr)),
|
||||
kind: PlotKind::House(plot::House::generate(
|
||||
land,
|
||||
&mut reseed(&mut rng),
|
||||
&site,
|
||||
door_tile,
|
||||
aabr,
|
||||
)),
|
||||
root_tile: aabr.center(),
|
||||
tiles: aabr_tiles(aabr).collect(),
|
||||
seed: rng.gen(),
|
||||
@ -267,7 +323,9 @@ impl Site {
|
||||
},
|
||||
// Guard tower
|
||||
2 => {
|
||||
if let Some((aabr, _)) = attempt(10, || site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))) {
|
||||
if let Some((aabr, _)) = attempt(10, || {
|
||||
site.find_roadside_aabr(&mut rng, 4..4, Extent2::new(2, 2))
|
||||
}) {
|
||||
let plot = site.create_plot(Plot {
|
||||
kind: PlotKind::Castle,
|
||||
root_tile: aabr.center(),
|
||||
@ -285,28 +343,26 @@ impl Site {
|
||||
3 => {
|
||||
attempt(10, || {
|
||||
let search_pos = attempt(16, || {
|
||||
let tile = (Vec2::new(
|
||||
rng.gen_range(-1.0..1.0),
|
||||
rng.gen_range(-1.0..1.0),
|
||||
).normalized() * rng.gen_range(16.0..48.0)).map(|e| e as i32);
|
||||
let tile =
|
||||
(Vec2::new(rng.gen_range(-1.0..1.0), rng.gen_range(-1.0..1.0))
|
||||
.normalized()
|
||||
* rng.gen_range(16.0..48.0))
|
||||
.map(|e| e as i32);
|
||||
|
||||
if site
|
||||
.plazas
|
||||
.iter()
|
||||
.all(|&p| site.plot(p).root_tile.distance_squared(tile) > 20i32.pow(2))
|
||||
&& rng.gen_range(0..48) > tile.map(|e| e.abs()).reduce_max()
|
||||
if site.plazas.iter().all(|&p| {
|
||||
site.plot(p).root_tile.distance_squared(tile) > 20i32.pow(2)
|
||||
}) && rng.gen_range(0..48) > tile.map(|e| e.abs()).reduce_max()
|
||||
{
|
||||
Some(tile)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(Vec2::zero);
|
||||
.unwrap_or_else(Vec2::zero);
|
||||
|
||||
site.tiles.find_near(
|
||||
search_pos,
|
||||
|center, _| site.tiles.grow_organic(&mut rng, center, 12..64).ok()
|
||||
)
|
||||
site.tiles.find_near(search_pos, |center, _| {
|
||||
site.tiles.grow_organic(&mut rng, center, 12..64).ok()
|
||||
})
|
||||
})
|
||||
.map(|(tiles, _)| {
|
||||
for tile in tiles {
|
||||
@ -319,7 +375,9 @@ impl Site {
|
||||
},
|
||||
// Castle
|
||||
4 if castles < 1 => {
|
||||
if let Some((aabr, _)) = attempt(10, || site.find_roadside_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16))) {
|
||||
if let Some((aabr, _)) = attempt(10, || {
|
||||
site.find_roadside_aabr(&mut rng, 16 * 16..18 * 18, Extent2::new(16, 16))
|
||||
}) {
|
||||
let plot = site.create_plot(Plot {
|
||||
kind: PlotKind::Castle,
|
||||
root_tile: aabr.center(),
|
||||
@ -337,22 +395,38 @@ impl Site {
|
||||
kind: TileKind::Castle,
|
||||
plot: Some(plot),
|
||||
};
|
||||
site.tiles.set(Vec2::new(aabr.min.x, aabr.min.y), tower.clone());
|
||||
site.tiles.set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower.clone());
|
||||
site.tiles.set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower.clone());
|
||||
site.tiles.set(Vec2::new(aabr.max.x - 1, aabr.max.y - 1), tower.clone());
|
||||
site.tiles
|
||||
.set(Vec2::new(aabr.min.x, aabr.min.y), tower.clone());
|
||||
site.tiles
|
||||
.set(Vec2::new(aabr.max.x - 1, aabr.min.y), tower.clone());
|
||||
site.tiles
|
||||
.set(Vec2::new(aabr.min.x, aabr.max.y - 1), tower.clone());
|
||||
site.tiles
|
||||
.set(Vec2::new(aabr.max.x - 1, aabr.max.y - 1), tower.clone());
|
||||
|
||||
// Courtyard
|
||||
site.blit_aabr(Aabr { min: aabr.min + 1, max: aabr.max - 1 } , Tile {
|
||||
kind: TileKind::Road { a: 0, b: 0, w: 0 },
|
||||
plot: Some(plot),
|
||||
});
|
||||
site.blit_aabr(
|
||||
Aabr {
|
||||
min: aabr.min + 1,
|
||||
max: aabr.max - 1,
|
||||
},
|
||||
Tile {
|
||||
kind: TileKind::Road { a: 0, b: 0, w: 0 },
|
||||
plot: Some(plot),
|
||||
},
|
||||
);
|
||||
|
||||
// Keep
|
||||
site.blit_aabr(Aabr { min: aabr.center() - 3, max: aabr.center() + 3 }, Tile {
|
||||
kind: TileKind::Castle,
|
||||
plot: Some(plot),
|
||||
});
|
||||
site.blit_aabr(
|
||||
Aabr {
|
||||
min: aabr.center() - 3,
|
||||
max: aabr.center() + 3,
|
||||
},
|
||||
Tile {
|
||||
kind: TileKind::Castle,
|
||||
plot: Some(plot),
|
||||
},
|
||||
);
|
||||
|
||||
castles += 1;
|
||||
}
|
||||
@ -384,7 +458,12 @@ impl Site {
|
||||
let tile = self.tiles.get(tpos);
|
||||
let twpos = self.tile_wpos(tpos);
|
||||
let border = TILE_SIZE as i32;
|
||||
let cols = (-border..TILE_SIZE as i32 + border).map(|y| (-border..TILE_SIZE as i32 + border).map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))).flatten();
|
||||
let cols = (-border..TILE_SIZE as i32 + border)
|
||||
.map(|y| {
|
||||
(-border..TILE_SIZE as i32 + border)
|
||||
.map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))
|
||||
})
|
||||
.flatten();
|
||||
|
||||
match &tile.kind {
|
||||
TileKind::Empty | TileKind::Hazard(_) => {},
|
||||
@ -418,15 +497,16 @@ impl Site {
|
||||
// // .filter_map(|line| Some(line?.projected_point(wpos2df)))
|
||||
// // .min_by_key(|p| p.distance_squared(wpos2df) as i32);
|
||||
|
||||
// // let is_near_road = nearest_road.map_or(false, |r| r.distance_squared(wpos2df) < 3.0f32.powi(2));
|
||||
// // let is_near_road = nearest_road.map_or(false, |r|
|
||||
// r.distance_squared(wpos2df) < 3.0f32.powi(2));
|
||||
|
||||
// // if let Some(nearest_road) = nearest_road
|
||||
// // .filter(|r| r.distance_squared(wpos2df) < 6.0f32.powi(2))
|
||||
// // {
|
||||
// // let road_alt = canvas.col(nearest_road.map(|e| e.floor() as i32)).map_or(0, |col| col.alt as i32);
|
||||
// // (-4..5).for_each(|z| canvas.map(
|
||||
// // Vec3::new(wpos2d.x, wpos2d.y, road_alt + z),
|
||||
// // |b| if z > 0 {
|
||||
// // let road_alt = canvas.col(nearest_road.map(|e| e.floor() as
|
||||
// i32)).map_or(0, |col| col.alt as i32); //
|
||||
// (-4..5).for_each(|z| canvas.map( // Vec3::new(wpos2d.x,
|
||||
// wpos2d.y, road_alt + z), // |b| if z > 0 {
|
||||
// // Block::air(SpriteKind::Empty)
|
||||
// // } else {
|
||||
// // Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))
|
||||
@ -562,7 +642,9 @@ impl Site {
|
||||
|
||||
let tile_aabr = Aabr {
|
||||
min: self.wpos_tile_pos(canvas.wpos()) - 1,
|
||||
max: self.wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2) + 3, // Round up, uninclusive, border
|
||||
max: self
|
||||
.wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2)
|
||||
+ 3, // Round up, uninclusive, border
|
||||
};
|
||||
|
||||
// Don't double-generate the same plot per chunk!
|
||||
@ -621,10 +703,9 @@ fn wpos_is_hazard(land: &Land, wpos: Vec2<i32>) -> Option<HazardKind> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aabr_tiles(aabr: Aabr<i32>) -> impl Iterator<Item=Vec2<i32>> {
|
||||
pub fn aabr_tiles(aabr: Aabr<i32>) -> impl Iterator<Item = Vec2<i32>> {
|
||||
(0..aabr.size().h)
|
||||
.map(move |y| (0..aabr.size().w)
|
||||
.map(move |x| aabr.min + Vec2::new(x, y)))
|
||||
.map(move |y| (0..aabr.size().w).map(move |x| aabr.min + Vec2::new(x, y)))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
mod house;
|
||||
|
||||
pub use self::{
|
||||
house::House,
|
||||
};
|
||||
pub use self::house::House;
|
||||
|
||||
use super::*;
|
||||
use crate::util::DHashSet;
|
||||
|
@ -80,16 +80,22 @@ impl Structure for House {
|
||||
let mut pillars_y = prim(Primitive::Empty);
|
||||
for x in self.tile_aabr.min.x..self.tile_aabr.max.x + 2 {
|
||||
let pillar = prim(Primitive::Aabb(Aabb {
|
||||
min: site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y)).with_z(self.alt),
|
||||
max: (site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y + 1)) + Vec2::unit_x()).with_z(self.alt + roof),
|
||||
min: site
|
||||
.tile_wpos(Vec2::new(x, self.tile_aabr.min.y))
|
||||
.with_z(self.alt),
|
||||
max: (site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y + 1)) + Vec2::unit_x())
|
||||
.with_z(self.alt + roof),
|
||||
}));
|
||||
pillars_y = prim(Primitive::Or(pillars_y, pillar));
|
||||
}
|
||||
let mut pillars_x = prim(Primitive::Empty);
|
||||
for y in self.tile_aabr.min.y..self.tile_aabr.max.y + 2 {
|
||||
let pillar = prim(Primitive::Aabb(Aabb {
|
||||
min: site.tile_wpos(Vec2::new(self.tile_aabr.min.x, y)).with_z(self.alt),
|
||||
max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 1, y)) + Vec2::unit_y()).with_z(self.alt + roof),
|
||||
min: site
|
||||
.tile_wpos(Vec2::new(self.tile_aabr.min.x, y))
|
||||
.with_z(self.alt),
|
||||
max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 1, y)) + Vec2::unit_y())
|
||||
.with_z(self.alt + roof),
|
||||
}));
|
||||
pillars_x = prim(Primitive::Or(pillars_x, pillar));
|
||||
}
|
||||
@ -109,8 +115,12 @@ impl Structure for House {
|
||||
let mut windows = prim(Primitive::Empty);
|
||||
for y in self.tile_aabr.min.y..self.tile_aabr.max.y {
|
||||
let window = prim(Primitive::Aabb(Aabb {
|
||||
min: (site.tile_wpos(Vec2::new(self.tile_aabr.min.x, y)) + Vec2::unit_y() * 2).with_z(self.alt + height + 2),
|
||||
max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x, y + 1)) + Vec2::new(1, -1)).with_z(self.alt + height + 2 + window_height),
|
||||
min: (site.tile_wpos(Vec2::new(self.tile_aabr.min.x, y))
|
||||
+ Vec2::unit_y() * 2)
|
||||
.with_z(self.alt + height + 2),
|
||||
max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x, y + 1))
|
||||
+ Vec2::new(1, -1))
|
||||
.with_z(self.alt + height + 2 + window_height),
|
||||
}));
|
||||
windows = prim(Primitive::Or(windows, window));
|
||||
}
|
||||
@ -124,8 +134,12 @@ impl Structure for House {
|
||||
let mut windows = prim(Primitive::Empty);
|
||||
for x in self.tile_aabr.min.x..self.tile_aabr.max.x {
|
||||
let window = prim(Primitive::Aabb(Aabb {
|
||||
min: (site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y)) + Vec2::unit_x() * 2).with_z(self.alt + height + 2),
|
||||
max: (site.tile_wpos(Vec2::new(x + 1, self.tile_aabr.max.y)) + Vec2::new(-1, 1)).with_z(self.alt + height + 2 + window_height),
|
||||
min: (site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y))
|
||||
+ Vec2::unit_x() * 2)
|
||||
.with_z(self.alt + height + 2),
|
||||
max: (site.tile_wpos(Vec2::new(x + 1, self.tile_aabr.max.y))
|
||||
+ Vec2::new(-1, 1))
|
||||
.with_z(self.alt + height + 2 + window_height),
|
||||
}));
|
||||
windows = prim(Primitive::Or(windows, window));
|
||||
}
|
||||
|
@ -41,13 +41,15 @@ impl TileGrid {
|
||||
// WILL NOT EXPAND BOUNDS!
|
||||
pub fn get_mut(&mut self, tpos: Vec2<i32>) -> Option<&mut Tile> {
|
||||
let tpos = tpos + TILE_RADIUS as i32;
|
||||
self.zones.get_mut(tpos.map(|e| e.div_euclid(ZONE_SIZE as i32))).and_then(|zone| {
|
||||
zone.get_or_insert_with(|| {
|
||||
Grid::populate_from(Vec2::broadcast(ZONE_SIZE as i32), |_| None)
|
||||
self.zones
|
||||
.get_mut(tpos.map(|e| e.div_euclid(ZONE_SIZE as i32)))
|
||||
.and_then(|zone| {
|
||||
zone.get_or_insert_with(|| {
|
||||
Grid::populate_from(Vec2::broadcast(ZONE_SIZE as i32), |_| None)
|
||||
})
|
||||
.get_mut(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
|
||||
.map(|tile| tile.get_or_insert_with(|| Tile::empty()))
|
||||
})
|
||||
.get_mut(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
|
||||
.map(|tile| tile.get_or_insert_with(|| Tile::empty()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set(&mut self, tpos: Vec2<i32>, tile: Tile) -> Option<Tile> {
|
||||
@ -55,7 +57,11 @@ impl TileGrid {
|
||||
self.get_mut(tpos).map(|t| std::mem::replace(t, tile))
|
||||
}
|
||||
|
||||
pub fn find_near<R>(&self, tpos: Vec2<i32>, mut f: impl FnMut(Vec2<i32>, &Tile) -> Option<R>) -> Option<(R, Vec2<i32>)> {
|
||||
pub fn find_near<R>(
|
||||
&self,
|
||||
tpos: Vec2<i32>,
|
||||
mut f: impl FnMut(Vec2<i32>, &Tile) -> Option<R>,
|
||||
) -> Option<(R, Vec2<i32>)> {
|
||||
const MAX_SEARCH_RADIUS_BLOCKS: u32 = 70;
|
||||
const MAX_SEARCH_CELLS: u32 = ((MAX_SEARCH_RADIUS_BLOCKS / TILE_SIZE) * 2 + 1).pow(2);
|
||||
Spiral2d::new()
|
||||
@ -64,8 +70,16 @@ impl TileGrid {
|
||||
.find_map(|tpos| (&mut f)(tpos, self.get(tpos)).zip(Some(tpos)))
|
||||
}
|
||||
|
||||
pub fn grow_aabr(&self, center: Vec2<i32>, area_range: Range<u32>, min_dims: Extent2<u32>) -> Result<Aabr<i32>, Aabr<i32>> {
|
||||
let mut aabr = Aabr { min: center, max: center + 1 };
|
||||
pub fn grow_aabr(
|
||||
&self,
|
||||
center: Vec2<i32>,
|
||||
area_range: Range<u32>,
|
||||
min_dims: Extent2<u32>,
|
||||
) -> Result<Aabr<i32>, Aabr<i32>> {
|
||||
let mut aabr = Aabr {
|
||||
min: center,
|
||||
max: center + 1,
|
||||
};
|
||||
|
||||
if !self.get(center).is_empty() {
|
||||
return Err(aabr);
|
||||
@ -75,27 +89,42 @@ impl TileGrid {
|
||||
for i in 0..32 {
|
||||
if i - last_growth >= 4 {
|
||||
break;
|
||||
} else if aabr.size().product() + if i % 2 == 0 { aabr.size().h } else { aabr.size().w } > area_range.end as i32 {
|
||||
} else if aabr.size().product()
|
||||
+ if i % 2 == 0 {
|
||||
aabr.size().h
|
||||
} else {
|
||||
aabr.size().w
|
||||
}
|
||||
> area_range.end as i32
|
||||
{
|
||||
break;
|
||||
} else {
|
||||
// `center.sum()` to avoid biasing certain directions
|
||||
match (i + center.sum().abs()) % 4 {
|
||||
0 if (aabr.min.y..aabr.max.y + 1).all(|y| self.get(Vec2::new(aabr.max.x, y)).is_empty()) => {
|
||||
0 if (aabr.min.y..aabr.max.y + 1)
|
||||
.all(|y| self.get(Vec2::new(aabr.max.x, y)).is_empty()) =>
|
||||
{
|
||||
aabr.max.x += 1;
|
||||
last_growth = i;
|
||||
},
|
||||
1 if (aabr.min.x..aabr.max.x + 1).all(|x| self.get(Vec2::new(x, aabr.max.y)).is_empty()) => {
|
||||
}
|
||||
1 if (aabr.min.x..aabr.max.x + 1)
|
||||
.all(|x| self.get(Vec2::new(x, aabr.max.y)).is_empty()) =>
|
||||
{
|
||||
aabr.max.y += 1;
|
||||
last_growth = i;
|
||||
},
|
||||
2 if (aabr.min.y..aabr.max.y + 1).all(|y| self.get(Vec2::new(aabr.min.x - 1, y)).is_empty()) => {
|
||||
}
|
||||
2 if (aabr.min.y..aabr.max.y + 1)
|
||||
.all(|y| self.get(Vec2::new(aabr.min.x - 1, y)).is_empty()) =>
|
||||
{
|
||||
aabr.min.x -= 1;
|
||||
last_growth = i;
|
||||
},
|
||||
3 if (aabr.min.x..aabr.max.x + 1).all(|x| self.get(Vec2::new(x, aabr.min.y - 1)).is_empty()) => {
|
||||
}
|
||||
3 if (aabr.min.x..aabr.max.x + 1)
|
||||
.all(|x| self.get(Vec2::new(x, aabr.min.y - 1)).is_empty()) =>
|
||||
{
|
||||
aabr.min.y -= 1;
|
||||
last_growth = i;
|
||||
},
|
||||
}
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -111,7 +140,12 @@ impl TileGrid {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn grow_organic(&self, rng: &mut impl Rng, center: Vec2<i32>, area_range: Range<u32>) -> Result<DHashSet<Vec2<i32>>, DHashSet<Vec2<i32>>> {
|
||||
pub fn grow_organic(
|
||||
&self,
|
||||
rng: &mut impl Rng,
|
||||
center: Vec2<i32>,
|
||||
area_range: Range<u32>,
|
||||
) -> Result<DHashSet<Vec2<i32>>, DHashSet<Vec2<i32>>> {
|
||||
let mut tiles = DHashSet::default();
|
||||
let mut open = Vec::new();
|
||||
|
||||
@ -165,26 +199,16 @@ impl Tile {
|
||||
}
|
||||
|
||||
/// Create a tile that is not associated with any plot.
|
||||
pub const fn free(kind: TileKind) -> Self {
|
||||
Self {
|
||||
kind,
|
||||
plot: None,
|
||||
}
|
||||
}
|
||||
pub const fn free(kind: TileKind) -> Self { Self { kind, plot: None } }
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
|
||||
|
||||
pub fn is_road(&self) -> bool {
|
||||
matches!(self.kind, TileKind::Road { .. })
|
||||
}
|
||||
pub fn is_road(&self) -> bool { matches!(self.kind, TileKind::Road { .. }) }
|
||||
|
||||
pub fn is_obstacle(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
TileKind::Hazard(_)
|
||||
| TileKind::Building
|
||||
| TileKind::Castle
|
||||
| TileKind::Wall
|
||||
TileKind::Hazard(_) | TileKind::Building | TileKind::Castle | TileKind::Wall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::ops::Range;
|
||||
use vek::*;
|
||||
|
||||
/// Return a value between 0 and 1 corresponding to how close to the centre of `range` `x` is.
|
||||
/// The exact function used is left unspecified, but it shall have the shape of a bell-like curve.
|
||||
/// This function is required to return `0` (or a value extremely close to `0`) when `x` is outside of `range`.
|
||||
/// Return a value between 0 and 1 corresponding to how close to the centre of
|
||||
/// `range` `x` is. The exact function used is left unspecified, but it shall
|
||||
/// have the shape of a bell-like curve. This function is required to return `0`
|
||||
/// (or a value extremely close to `0`) when `x` is outside of `range`.
|
||||
pub fn close(x: f32, range: Range<f32>) -> f32 {
|
||||
let mean = (range.start + range.end) / 2.0;
|
||||
let width = (range.end - range.start) / 2.0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub mod fast_noise;
|
||||
pub mod math;
|
||||
pub mod map_vec;
|
||||
pub mod math;
|
||||
pub mod random;
|
||||
pub mod sampler;
|
||||
pub mod seed_expan;
|
||||
|
Loading…
Reference in New Issue
Block a user