From 29705b0a1f34af19bf8968c9efc111b7c058a6bd Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Fri, 12 Feb 2021 20:30:30 +0000 Subject: [PATCH] Initial test site generation --- world/src/site2/mod.rs | 57 +++++++++++++++++++++++++++----- world/src/site2/tile.rs | 72 +++++++++++++++++++++++++++++++++++------ 2 files changed, 111 insertions(+), 18 deletions(-) diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 05758419f5..8ea1b8b28c 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -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::::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) -> &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)) + )), + _ => {}, + } + }); } } diff --git a/world/src/site2/tile.rs b/world/src/site2/tile.rs index 7f7483b4f8..75e5c552d4 100644 --- a/world/src/site2/tile.rs +++ b/world/src/site2/tile.rs @@ -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) -> 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, f: impl Fn(&Tile) -> bool) -> Option> { + pub fn find_near(&self, tpos: Vec2, f: impl Fn(Vec2, &Tile) -> bool) -> Option> { 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, area_range: Range, min_dims: Extent2) -> Result, Aabr> { + 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>, + pub(crate) kind: TileKind, + pub(crate) plot: Option>, } 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 } }