mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Switched to 6x6 tiles, more natural paths
This commit is contained in:
parent
a229d65932
commit
02d86f0fb0
@ -65,8 +65,8 @@ impl<'a> Canvas<'a> {
|
||||
/// inner `CanvasInfo` such that it may be used independently.
|
||||
pub fn info(&mut self) -> CanvasInfo<'a> { self.info }
|
||||
|
||||
pub fn get(&mut self, pos: Vec3<i32>) -> Option<Block> {
|
||||
self.chunk.get(pos - self.wpos()).ok().copied()
|
||||
pub fn get(&mut self, pos: Vec3<i32>) -> Block {
|
||||
self.chunk.get(pos - self.wpos()).ok().copied().unwrap_or(Block::empty())
|
||||
}
|
||||
|
||||
pub fn set(&mut self, pos: Vec3<i32>, block: Block) {
|
||||
|
@ -99,7 +99,7 @@ pub fn apply_paths_to(canvas: &mut Canvas) {
|
||||
let head_space = path.head_space(path_dist);
|
||||
for z in inset..inset + head_space {
|
||||
let pos = Vec3::new(wpos2d.x, wpos2d.y, surface_z + z);
|
||||
if canvas.get(pos).unwrap().kind() != BlockKind::Water {
|
||||
if canvas.get(pos).kind() != BlockKind::Water {
|
||||
let _ = canvas.set(pos, EMPTY_AIR);
|
||||
}
|
||||
}
|
||||
@ -135,11 +135,7 @@ pub fn apply_caves_to(canvas: &mut Canvas, rng: &mut impl Rng) {
|
||||
{
|
||||
// If the block a little above is liquid, we should stop carving out the cave in
|
||||
// order to leave a ceiling, and not floating water
|
||||
if canvas
|
||||
.get(Vec3::new(wpos2d.x, wpos2d.y, z + 2))
|
||||
.map(|b| b.is_liquid())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if canvas.get(Vec3::new(wpos2d.x, wpos2d.y, z + 2)).is_liquid() {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -165,11 +161,7 @@ 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))
|
||||
.map(|b| b.is_filled())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
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,17 +577,11 @@ 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))
|
||||
.map(|b| b.is_solid())
|
||||
.unwrap_or(false)
|
||||
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))
|
||||
.map(|b| !b.is_solid())
|
||||
.unwrap_or(true)
|
||||
!canvas.get(Vec3::new(wpos2d.x, wpos2d.y, alt + z)).is_solid()
|
||||
})
|
||||
})
|
||||
{
|
||||
|
@ -23,9 +23,19 @@ use common::{
|
||||
};
|
||||
use hashbrown::hash_map::DefaultHashBuilder;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use vek::*;
|
||||
use std::ops::Range;
|
||||
|
||||
/// 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 {
|
||||
pub(crate) origin: Vec2<i32>,
|
||||
@ -72,11 +82,12 @@ impl Site {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_road(&mut self, land: &Land, rng: &mut impl Rng, a: Vec2<i32>, b: Vec2<i32>, w: i32) -> 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>| {
|
||||
for y in 0..w {
|
||||
for x in 0..w {
|
||||
for y in range.clone() {
|
||||
for x in range.clone() {
|
||||
if self.tiles.get(*tile + Vec2::new(x, y)).is_obstacle() {
|
||||
return 1000.0;
|
||||
}
|
||||
@ -105,11 +116,15 @@ impl Site {
|
||||
|
||||
self.roads.push(plot);
|
||||
|
||||
for &tile in path.iter() {
|
||||
for y in 0..w {
|
||||
for x in 0..w {
|
||||
for (i, &tile) in path.iter().enumerate() {
|
||||
for y in range.clone() {
|
||||
for x in range.clone() {
|
||||
self.tiles.set(tile + Vec2::new(x, y), Tile {
|
||||
kind: TileKind::Road,
|
||||
kind: TileKind::Road {
|
||||
a: i.saturating_sub(1) as u16,
|
||||
b: (i + 1).min(path.len() - 1) as u16,
|
||||
w,
|
||||
},
|
||||
plot: Some(plot),
|
||||
});
|
||||
}
|
||||
@ -125,10 +140,10 @@ impl Site {
|
||||
|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)).kind == TileKind::Road)
|
||||
|| (aabr.min.x..aabr.max.x).any(|x| self.tiles.get(Vec2::new(x, aabr.max.y)).kind == TileKind::Road)
|
||||
|| (aabr.min.y..aabr.max.y).any(|y| self.tiles.get(Vec2::new(aabr.min.x - 1, y)).kind == TileKind::Road)
|
||||
|| (aabr.min.y..aabr.max.y).any(|y| self.tiles.get(Vec2::new(aabr.max.x, y)).kind == TileKind::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())
|
||||
}),
|
||||
)
|
||||
}
|
||||
@ -171,7 +186,7 @@ impl Site {
|
||||
});
|
||||
self.plazas.push(plaza);
|
||||
self.blit_aabr(aabr, Tile {
|
||||
kind: TileKind::Road,
|
||||
kind: TileKind::Road { a: 0, b: 0, w: 0 },
|
||||
plot: Some(plaza),
|
||||
});
|
||||
|
||||
@ -209,6 +224,8 @@ impl Site {
|
||||
}
|
||||
|
||||
pub fn generate(land: &Land, rng: &mut impl Rng, origin: Vec2<i32>) -> Self {
|
||||
let mut rng = reseed(rng);
|
||||
|
||||
let mut site = Site {
|
||||
origin,
|
||||
..Site::default()
|
||||
@ -216,34 +233,25 @@ impl Site {
|
||||
|
||||
site.demarcate_obstacles(land);
|
||||
|
||||
site.make_plaza(land, rng);
|
||||
site.make_plaza(land, &mut rng);
|
||||
|
||||
let build_chance = Lottery::from(vec![
|
||||
(1.0, 0),
|
||||
(48.0, 1),
|
||||
(64.0, 1),
|
||||
(5.0, 2),
|
||||
(20.0, 3),
|
||||
(1.0, 4),
|
||||
(0.75, 4),
|
||||
]);
|
||||
|
||||
let mut castles = 0;
|
||||
|
||||
for _ in 0..1000 {
|
||||
if site.plots.len() - site.plazas.len() > 80 {
|
||||
break;
|
||||
}
|
||||
|
||||
for _ in 0..120 {
|
||||
match *build_chance.choose_seeded(rng.gen()) {
|
||||
// Plaza
|
||||
0 => {
|
||||
site.make_plaza(land, rng);
|
||||
},
|
||||
// House
|
||||
1 => {
|
||||
let size = (2.0 + rng.gen::<f32>().powf(8.0) * 3.0).round() as u32;
|
||||
if let Some((aabr, door_tile)) = attempt(10, || site.find_roadside_aabr(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, 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(),
|
||||
@ -253,11 +261,13 @@ impl Site {
|
||||
kind: TileKind::Building,
|
||||
plot: Some(plot),
|
||||
});
|
||||
} else {
|
||||
site.make_plaza(land, &mut rng);
|
||||
}
|
||||
},
|
||||
// Guard tower
|
||||
2 => {
|
||||
if let Some((aabr, _)) = attempt(10, || site.find_roadside_aabr(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(),
|
||||
@ -278,7 +288,7 @@ impl Site {
|
||||
let tile = (Vec2::new(
|
||||
rng.gen_range(-1.0..1.0),
|
||||
rng.gen_range(-1.0..1.0),
|
||||
).normalized() * rng.gen_range(32.0..48.0)).map(|e| e as i32);
|
||||
).normalized() * rng.gen_range(16.0..48.0)).map(|e| e as i32);
|
||||
|
||||
if site
|
||||
.plazas
|
||||
@ -292,20 +302,24 @@ impl Site {
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(Vec2::zero);
|
||||
|
||||
site.tiles.find_near(
|
||||
search_pos,
|
||||
|center, _| site.tiles.grow_aabr(center, 9..25, Extent2::new(3, 3)).ok())
|
||||
|center, _| site.tiles.grow_organic(&mut rng, center, 12..64).ok()
|
||||
)
|
||||
})
|
||||
.map(|(aabr, _)| {
|
||||
site.blit_aabr(aabr, Tile {
|
||||
.map(|(tiles, _)| {
|
||||
for tile in tiles {
|
||||
site.tiles.set(tile, Tile {
|
||||
kind: TileKind::Field,
|
||||
plot: None,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// Castle
|
||||
_ if castles < 1 => {
|
||||
if let Some((aabr, _)) = attempt(10, || site.find_roadside_aabr(rng, 16 * 16..18 * 18, Extent2::new(16, 16))) {
|
||||
4 if castles < 1 => {
|
||||
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(),
|
||||
@ -330,7 +344,7 @@ impl Site {
|
||||
|
||||
// Courtyard
|
||||
site.blit_aabr(Aabr { min: aabr.min + 1, max: aabr.max - 1 } , Tile {
|
||||
kind: TileKind::Road,
|
||||
kind: TileKind::Road { a: 0, b: 0, w: 0 },
|
||||
plot: Some(plot),
|
||||
});
|
||||
|
||||
@ -368,52 +382,184 @@ impl Site {
|
||||
|
||||
pub fn render_tile(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng, tpos: Vec2<i32>) {
|
||||
let tile = self.tiles.get(tpos);
|
||||
let twpos = self.tile_center_wpos(tpos);
|
||||
let cols = (-(TILE_SIZE as i32)..TILE_SIZE as i32 * 2).map(|y| (-(TILE_SIZE as i32)..TILE_SIZE as i32 * 2).map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))).flatten();
|
||||
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();
|
||||
|
||||
match &tile.kind {
|
||||
TileKind::Empty | TileKind::Hazard(_) => {},
|
||||
TileKind::Road => {
|
||||
let near_roads = CARDINALS
|
||||
.map(|rpos| if self.tiles.get(tpos + rpos) == tile {
|
||||
Some(LineSegment2 {
|
||||
start: self.tile_center_wpos(tpos).map(|e| e as f32),
|
||||
end: self.tile_center_wpos(tpos + rpos).map(|e| e as f32),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
});
|
||||
// TileKind::Road => {
|
||||
// let near_roads = CARDINALS
|
||||
// .map(|rpos| if self.tiles.get(tpos + rpos) == tile {
|
||||
// Some(LineSegment2 {
|
||||
// start: self.tile_center_wpos(tpos).map(|e| e as f32),
|
||||
// end: self.tile_center_wpos(tpos + rpos).map(|e| e as f32),
|
||||
// })
|
||||
// } else {
|
||||
// None
|
||||
// });
|
||||
|
||||
cols.for_each(|(wpos2d, offs)| {
|
||||
let wpos2df = wpos2d.map(|e| e as f32);
|
||||
let nearest_road = near_roads
|
||||
.iter()
|
||||
.copied()
|
||||
.filter_map(|line| Some(line?.projected_point(wpos2df)))
|
||||
.min_by_key(|p| p.distance_squared(wpos2df) as i32);
|
||||
// let near_roads = CARDINALS
|
||||
// .iter()
|
||||
// .filter_map(|rpos| if self.tiles.get(tpos + rpos) == tile {
|
||||
// Some(Aabr {
|
||||
// min: self.tile_wpos(tpos).map(|e| e as f32),
|
||||
// max: self.tile_wpos(tpos + 1).map(|e| e as f32),
|
||||
// })
|
||||
// } else {
|
||||
// None
|
||||
// });
|
||||
|
||||
let is_near_road = nearest_road.map_or(false, |r| r.distance_squared(wpos2df) < 3.0f32.powi(2));
|
||||
// // cols.for_each(|(wpos2d, offs)| {
|
||||
// // let wpos2df = wpos2d.map(|e| e as f32);
|
||||
// // let nearest_road = near_roads
|
||||
// // .iter()
|
||||
// // .copied()
|
||||
// // .filter_map(|line| Some(line?.projected_point(wpos2df)))
|
||||
// // .min_by_key(|p| p.distance_squared(wpos2df) as i32);
|
||||
|
||||
if let Some(nearest_road) = nearest_road
|
||||
.filter(|r| r.distance_squared(wpos2df) < 4.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 {
|
||||
Block::air(SpriteKind::Empty)
|
||||
} else {
|
||||
Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))
|
||||
},
|
||||
));
|
||||
}
|
||||
});
|
||||
},
|
||||
// // 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 {
|
||||
// // Block::air(SpriteKind::Empty)
|
||||
// // } else {
|
||||
// // Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))
|
||||
// // },
|
||||
// // ));
|
||||
// // }
|
||||
// // });
|
||||
|
||||
// cols.for_each(|(wpos2d, offs)| {
|
||||
// let wpos2df = wpos2d.map(|e| e as f32);
|
||||
|
||||
// let dist = near_roads
|
||||
// .clone()
|
||||
// .map(|aabr| aabr.distance_to_point(wpos2df).powi(2))
|
||||
// .sum::<f32>()
|
||||
// .sqrt();
|
||||
|
||||
// if dist < 4.0 {
|
||||
// let mut z = canvas.col(wpos2d).map_or(0, |col| col.alt as i32) + 8;
|
||||
// for _ in 0..16 {
|
||||
// let pos = Vec3::new(wpos2d.x, wpos2d.y, z);
|
||||
// z -= 1;
|
||||
// if !canvas.get(pos).is_filled() {
|
||||
// canvas.map(pos, |b| b.with_sprite(SpriteKind::Empty));
|
||||
// } else {
|
||||
// canvas.set(pos, Block::empty());
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// for _ in 0..2 {
|
||||
// let pos = Vec3::new(wpos2d.x, wpos2d.y, z);
|
||||
// z -= 1;
|
||||
// canvas.set(pos, Block::new(BlockKind::Rock, Rgb::new(55, 45, 50)));
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
||||
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||
|
||||
let tpos = self.wpos_tile_pos(wpos2d);
|
||||
let near_roads = SQUARE_9
|
||||
.iter()
|
||||
.filter_map(|rpos| {
|
||||
let tile = self.tiles.get(tpos + rpos);
|
||||
if let TileKind::Road { a, b, w } = &tile.kind {
|
||||
if let Some(PlotKind::Road(path)) = tile.plot.map(|p| &self.plot(p).kind) {
|
||||
Some((LineSegment2 {
|
||||
start: self.tile_center_wpos(path.nodes()[*a as usize]).map(|e| e as f32),
|
||||
end: self.tile_center_wpos(path.nodes()[*b as usize]).map(|e| e as f32),
|
||||
}, *w))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let wpos2df = wpos2d.map(|e| e as f32);
|
||||
let dist = near_roads
|
||||
.map(|(line, w)| (line.distance_to_point(wpos2df) - w as f32 * 2.0).max(0.0))
|
||||
.min_by_key(|d| (*d * 100.0) as i32);
|
||||
|
||||
if dist.map_or(false, |d| d <= 0.75) {
|
||||
let mut z = canvas.col(wpos2d).map_or(0, |col| col.alt as i32) + 8;
|
||||
for _ in 0..16 {
|
||||
let pos = Vec3::new(wpos2d.x, wpos2d.y, z);
|
||||
z -= 1;
|
||||
if !canvas.get(pos).is_filled() {
|
||||
canvas.map(pos, |b| b.with_sprite(SpriteKind::Empty));
|
||||
} else {
|
||||
canvas.set(pos, Block::empty());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0..2 {
|
||||
let pos = Vec3::new(wpos2d.x, wpos2d.y, z);
|
||||
z -= 1;
|
||||
canvas.set(pos, Block::new(BlockKind::Rock, Rgb::new(55, 45, 50)));
|
||||
}
|
||||
}
|
||||
|
||||
let tile = self.wpos_tile(wpos2d);
|
||||
let seed = tile.plot.map_or(0, |p| self.plot(p).seed);
|
||||
match tile.kind {
|
||||
TileKind::Field /*| TileKind::Road*/ => (-4..5).for_each(|z| canvas.map(
|
||||
Vec3::new(wpos2d.x, wpos2d.y, col.alt as i32 + z),
|
||||
|b| if [
|
||||
BlockKind::Grass,
|
||||
BlockKind::Earth,
|
||||
BlockKind::Sand,
|
||||
BlockKind::Snow,
|
||||
BlockKind::Rock,
|
||||
]
|
||||
.contains(&b.kind()) {
|
||||
match tile.kind {
|
||||
TileKind::Field => Block::new(BlockKind::Earth, Rgb::new(40, 5 + (seed % 32) as u8, 0)),
|
||||
TileKind::Road { .. } => Block::new(BlockKind::Rock, Rgb::new(55, 45, 65)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
b.with_sprite(SpriteKind::Empty)
|
||||
},
|
||||
)),
|
||||
// TileKind::Building => {
|
||||
// let base_alt = tile.plot.map(|p| self.plot(p)).map_or(col.alt as i32, |p| p.base_alt);
|
||||
// for z in base_alt - 12..base_alt + 16 {
|
||||
// canvas.set(
|
||||
// Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||
// Block::new(BlockKind::Wood, Rgb::new(180, 90 + (seed % 64) as u8, 120))
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// TileKind::Castle | TileKind::Wall => {
|
||||
// let base_alt = tile.plot.map(|p| self.plot(p)).map_or(col.alt as i32, |p| p.base_alt);
|
||||
// for z in base_alt - 12..base_alt + if tile.kind == TileKind::Wall { 24 } else { 40 } {
|
||||
// canvas.set(
|
||||
// Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||
// Block::new(BlockKind::Wood, Rgb::new(40, 40, 55))
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
_ => {},
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
@ -457,51 +603,6 @@ impl Site {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// canvas.foreach_col(|canvas, wpos2d, col| {
|
||||
// let tile = self.wpos_tile(wpos2d);
|
||||
// let seed = tile.plot.map_or(0, |p| self.plot(p).seed);
|
||||
// match tile.kind {
|
||||
// TileKind::Field /*| TileKind::Road*/ => (-4..5).for_each(|z| canvas.map(
|
||||
// Vec3::new(wpos2d.x, wpos2d.y, col.alt as i32 + z),
|
||||
// |b| if [
|
||||
// BlockKind::Grass,
|
||||
// BlockKind::Earth,
|
||||
// BlockKind::Sand,
|
||||
// BlockKind::Snow,
|
||||
// BlockKind::Rock,
|
||||
// ]
|
||||
// .contains(&b.kind()) {
|
||||
// match tile.kind {
|
||||
// TileKind::Field => Block::new(BlockKind::Earth, Rgb::new(40, 5 + (seed % 32) as u8, 0)),
|
||||
// TileKind::Road => Block::new(BlockKind::Rock, Rgb::new(55, 45, 65)),
|
||||
// _ => unreachable!(),
|
||||
// }
|
||||
// } else {
|
||||
// b.with_sprite(SpriteKind::Empty)
|
||||
// },
|
||||
// )),
|
||||
// TileKind::Building => {
|
||||
// let base_alt = tile.plot.map(|p| self.plot(p)).map_or(col.alt as i32, |p| p.base_alt);
|
||||
// for z in base_alt - 12..base_alt + 16 {
|
||||
// canvas.set(
|
||||
// Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||
// Block::new(BlockKind::Wood, Rgb::new(180, 90 + (seed % 64) as u8, 120))
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// TileKind::Castle | TileKind::Wall => {
|
||||
// let base_alt = tile.plot.map(|p| self.plot(p)).map_or(col.alt as i32, |p| p.base_alt);
|
||||
// for z in base_alt - 12..base_alt + if tile.kind == TileKind::Wall { 24 } else { 40 } {
|
||||
// canvas.set(
|
||||
// Vec3::new(wpos2d.x, wpos2d.y, z),
|
||||
// Block::new(BlockKind::Wood, Rgb::new(40, 40, 55))
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// _ => {},
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ pub struct House {
|
||||
bounds: Aabr<i32>,
|
||||
alt: i32,
|
||||
levels: u32,
|
||||
roof_color: Rgb<u8>,
|
||||
}
|
||||
|
||||
impl House {
|
||||
@ -28,7 +29,19 @@ impl House {
|
||||
max: site.tile_wpos(tile_aabr.max),
|
||||
},
|
||||
alt: land.get_alt_approx(site.tile_center_wpos(door_tile)) as i32 + 2,
|
||||
levels: rng.gen_range(1..3),
|
||||
levels: rng.gen_range(1..2 + (tile_aabr.max - tile_aabr.min).product() / 6) as u32,
|
||||
roof_color: {
|
||||
let colors = [
|
||||
Rgb::new(21, 43, 48),
|
||||
Rgb::new(11, 23, 38),
|
||||
Rgb::new(45, 28, 21),
|
||||
Rgb::new(10, 55, 40),
|
||||
Rgb::new(5, 35, 15),
|
||||
Rgb::new(40, 5, 11),
|
||||
Rgb::new(55, 45, 11),
|
||||
];
|
||||
*colors.choose(rng).unwrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,24 +53,28 @@ impl Structure for House {
|
||||
mut prim: F,
|
||||
mut fill: G,
|
||||
) {
|
||||
let storey = 6;
|
||||
let storey = 5;
|
||||
let roof = storey * self.levels as i32;
|
||||
let foundations = 12;
|
||||
|
||||
// Walls
|
||||
let outer = prim(Primitive::Aabb(Aabb {
|
||||
min: self.bounds.min.with_z(self.alt - foundations),
|
||||
max: (self.bounds.max + 1).with_z(self.alt + roof),
|
||||
}));
|
||||
let inner = prim(Primitive::Aabb(Aabb {
|
||||
min: (self.bounds.min + 1).with_z(self.alt),
|
||||
max: self.bounds.max.with_z(self.alt + roof),
|
||||
}));
|
||||
let walls = prim(Primitive::Xor(outer, inner));
|
||||
let outer = prim(Primitive::Aabb(Aabb {
|
||||
min: self.bounds.min.with_z(self.alt - foundations),
|
||||
max: (self.bounds.max + 1).with_z(self.alt + roof),
|
||||
}));
|
||||
fill(Fill {
|
||||
prim: walls,
|
||||
prim: outer,
|
||||
block: Block::new(BlockKind::Rock, Rgb::new(181, 170, 148)),
|
||||
});
|
||||
fill(Fill {
|
||||
prim: inner,
|
||||
block: Block::empty(),
|
||||
});
|
||||
let walls = prim(Primitive::Xor(outer, inner));
|
||||
|
||||
// wall pillars
|
||||
let mut pillars_y = prim(Primitive::Empty);
|
||||
@ -79,12 +96,13 @@ impl Structure for House {
|
||||
let pillars = prim(Primitive::And(pillars_x, pillars_y));
|
||||
fill(Fill {
|
||||
prim: pillars,
|
||||
block: Block::new(BlockKind::Wood, Rgb::new(89, 44, 14)),
|
||||
block: Block::new(BlockKind::Wood, Rgb::new(55, 25, 8)),
|
||||
});
|
||||
|
||||
// For each storey...
|
||||
for i in 0..self.levels + 1 {
|
||||
let height = storey * i as i32;
|
||||
let window_height = storey - 3;
|
||||
|
||||
// Windows x axis
|
||||
{
|
||||
@ -92,7 +110,7 @@ impl Structure for House {
|
||||
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 + 5),
|
||||
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));
|
||||
}
|
||||
@ -107,7 +125,7 @@ impl Structure for House {
|
||||
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 + 5),
|
||||
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));
|
||||
}
|
||||
@ -156,7 +174,7 @@ impl Structure for House {
|
||||
},
|
||||
inset: roof_height,
|
||||
}),
|
||||
block: Block::new(BlockKind::Wood, Rgb::new(21, 43, 48)),
|
||||
block: Block::new(BlockKind::Wood, self.roof_color),
|
||||
});
|
||||
|
||||
// Foundations
|
||||
|
@ -1,8 +1,9 @@
|
||||
use super::*;
|
||||
use crate::util::DHashSet;
|
||||
use common::spiral::Spiral2d;
|
||||
use std::ops::Range;
|
||||
|
||||
pub const TILE_SIZE: u32 = 7;
|
||||
pub const TILE_SIZE: u32 = 6;
|
||||
pub const ZONE_SIZE: u32 = 16;
|
||||
pub const ZONE_RADIUS: u32 = 16;
|
||||
pub const TILE_RADIUS: u32 = ZONE_SIZE * ZONE_RADIUS;
|
||||
@ -54,13 +55,13 @@ impl TileGrid {
|
||||
self.get_mut(tpos).map(|t| std::mem::replace(t, tile))
|
||||
}
|
||||
|
||||
pub fn find_near<R>(&self, tpos: Vec2<i32>, f: impl Fn(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()
|
||||
.take(MAX_SEARCH_CELLS as usize)
|
||||
.map(|r| tpos + r)
|
||||
.find_map(|tpos| (&f)(tpos, self.get(tpos)).zip(Some(tpos)))
|
||||
.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>> {
|
||||
@ -71,7 +72,7 @@ impl TileGrid {
|
||||
};
|
||||
|
||||
let mut last_growth = 0;
|
||||
for i in 0.. {
|
||||
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 {
|
||||
@ -109,6 +110,33 @@ impl TileGrid {
|
||||
Err(aabr)
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
tiles.insert(center);
|
||||
open.push(center);
|
||||
|
||||
while tiles.len() < area_range.end as usize && !open.is_empty() {
|
||||
let tile = open.remove(rng.gen_range(0..open.len()));
|
||||
|
||||
for &rpos in CARDINALS.iter() {
|
||||
let neighbor = tile + rpos;
|
||||
|
||||
if self.get(neighbor).is_empty() && !tiles.contains(&neighbor) {
|
||||
tiles.insert(neighbor);
|
||||
open.push(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tiles.len() >= area_range.start as usize {
|
||||
Ok(tiles)
|
||||
} else {
|
||||
Err(tiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
@ -116,7 +144,7 @@ pub enum TileKind {
|
||||
Empty,
|
||||
Hazard(HazardKind),
|
||||
Field,
|
||||
Road,
|
||||
Road { a: u16, b: u16, w: u16 },
|
||||
Building,
|
||||
Castle,
|
||||
Wall,
|
||||
@ -146,6 +174,10 @@ impl Tile {
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
|
||||
|
||||
pub fn is_road(&self) -> bool {
|
||||
matches!(self.kind, TileKind::Road { .. })
|
||||
}
|
||||
|
||||
pub fn is_obstacle(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
|
Loading…
Reference in New Issue
Block a user