Initial test site generation

This commit is contained in:
Joshua Barretto 2021-02-12 20:30:30 +00:00
parent 717bbbf23e
commit 3aff271a05
2 changed files with 111 additions and 18 deletions

View File

@ -3,14 +3,17 @@ mod tile;
use self::{
plot::{Plot, PlotKind},
tile::TileGrid,
tile::{TileGrid, Tile, TileKind, TILE_SIZE},
};
use crate::{
site::SpawnRules,
util::Grid,
Canvas,
};
use common::store::{Id, Store};
use common::{
terrain::{Block, BlockKind, SpriteKind},
store::{Id, Store},
};
use rand::prelude::*;
use vek::*;
@ -52,24 +55,62 @@ impl Site {
pub fn generate(rng: &mut impl Rng) -> Self {
let mut site = Site::default();
for i in 0..10 {
for i in 0..100 {
let dir = Vec2::<f32>::zero()
.map(|_| rng.gen_range(-1.0..1.0))
.normalized();
let search_pos = (dir * 32.0).map(|e| e as i32);
let search_pos = (dir * rng.gen_range(0.0f32..1.0).powf(2.0) * 24.0).map(|e| e as i32);
site.tiles
.find_near(search_pos, |tile| tile.is_empty())
.map(|center| {
// TODO
.find_near(search_pos, |_, tile| tile.is_empty())
.and_then(|center| site.tiles.grow_aabr(center, 6..16, Extent2::new(2, 2)).ok())
.map(|aabr| {
let tile = match i % 2 {
0 => TileKind::Farmland { seed: i },
_ => TileKind::Building { levels: 1 + i % 3 },
};
for x in 0..aabr.size().w {
for y in 0..aabr.size().h {
let pos = aabr.min + Vec2::new(x, y);
site.tiles.set(pos, Tile::free(tile.clone()));
}
}
});
}
site
}
pub fn wpos_tile(&self, wpos2d: Vec2<i32>) -> &Tile {
self.tiles.get((wpos2d - self.origin).map(|e| e.div_euclid(TILE_SIZE as i32)))
}
pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
// TODO
canvas.foreach_col(|canvas, wpos2d, col| {
match self.wpos_tile(wpos2d).kind {
TileKind::Farmland { seed } => (-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()) {
Block::new(BlockKind::Earth, Rgb::new(40, 5 + (seed % 32) as u8, 0))
} else {
b.with_sprite(SpriteKind::Empty)
},
)),
TileKind::Building { levels } => (-4..7 * levels as i32).for_each(|z| canvas.set(
Vec3::new(wpos2d.x, wpos2d.y, col.alt as i32 + z),
Block::new(BlockKind::Wood, Rgb::new(180, 150, 120))
)),
_ => {},
}
});
}
}

View File

@ -1,5 +1,6 @@
use super::*;
use common::spiral::Spiral2d;
use std::ops::Range;
pub const TILE_SIZE: u32 = 7;
pub const ZONE_SIZE: u32 = 16;
@ -25,7 +26,7 @@ impl TileGrid {
let tpos = tpos + TILE_RADIUS as i32;
self.zones
.get(tpos)
.get(tpos.map(|e| e.div_euclid(ZONE_SIZE as i32)))
.and_then(|zone| {
zone.as_ref()?
.get(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
@ -36,9 +37,9 @@ impl TileGrid {
pub fn get_mut(&mut self, tpos: Vec2<i32>) -> Option<&mut Tile> {
let tpos = tpos + TILE_RADIUS as i32;
self.zones.get_mut(tpos).and_then(|zone| {
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_RADIUS as i32 * 2 + 1), |_| None)
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()))
@ -49,23 +50,66 @@ impl TileGrid {
self.get_mut(tpos).map(|t| std::mem::replace(t, tile))
}
pub fn find_near(&self, tpos: Vec2<i32>, f: impl Fn(&Tile) -> bool) -> Option<Vec2<i32>> {
pub fn find_near(&self, tpos: Vec2<i32>, f: impl Fn(Vec2<i32>, &Tile) -> bool) -> Option<Vec2<i32>> {
const MAX_SEARCH_RADIUS_BLOCKS: u32 = 256;
const MAX_SEARCH_CELLS: u32 = ((MAX_SEARCH_RADIUS_BLOCKS / TILE_SIZE) * 2).pow(2);
Spiral2d::new().take(MAX_SEARCH_CELLS as usize).map(|r| tpos + r).find(|tpos| (&f)(self.get(*tpos)))
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(|tpos| (&f)(*tpos, self.get(*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::new_empty(center);
let mut last_growth = 0;
for i in 0.. {
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 {
break;
} else {
match i % 4 {
0 if (aabr.min.y..aabr.max.y).all(|y| self.get(Vec2::new(aabr.max.x + 1, y)).is_empty()) => {
aabr.max.x += 1;
last_growth = i;
},
1 if (aabr.min.x..aabr.max.x).all(|x| self.get(Vec2::new(x, aabr.max.y + 1)).is_empty()) => {
aabr.max.y += 1;
last_growth = i;
},
2 if (aabr.min.y..aabr.max.y).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).all(|x| self.get(Vec2::new(x, aabr.min.y - 1)).is_empty()) => {
aabr.min.y -= 1;
last_growth = i;
},
_ => {},
}
}
}
if aabr.size().product() as u32 >= area_range.start
&& aabr.size().w as u32 >= min_dims.w
&& aabr.size().h as u32 >= min_dims.h
{
Ok(aabr)
} else {
Err(aabr)
}
}
}
#[derive(PartialEq)]
#[derive(Clone, PartialEq)]
pub enum TileKind {
Empty,
Farmland,
Farmland { seed: u32 },
Building { levels: u32 },
}
#[derive(Clone)]
pub struct Tile {
kind: TileKind,
plot: Option<Id<Plot>>,
pub(crate) kind: TileKind,
pub(crate) plot: Option<Id<Plot>>,
}
impl Tile {
@ -76,5 +120,13 @@ impl Tile {
}
}
/// Create a tile that is not associated with any plot.
pub const fn free(kind: TileKind) -> Self {
Self {
kind,
plot: None,
}
}
pub fn is_empty(&self) -> bool { self.kind == TileKind::Empty }
}