mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added town road construction
This commit is contained in:
parent
aaade23eef
commit
d42485238e
@ -2,7 +2,7 @@ mod natural;
|
||||
|
||||
use crate::{
|
||||
column::{ColumnGen, ColumnSample, StructureData},
|
||||
generator::TownGen,
|
||||
generator::{Generator, TownGen},
|
||||
util::{HashCache, RandomField, Sampler, SamplerMut},
|
||||
World, CONFIG,
|
||||
};
|
||||
@ -137,6 +137,7 @@ impl<'a> BlockGen<'a> {
|
||||
column_gen,
|
||||
} = self;
|
||||
|
||||
let sample = &z_cache?.sample;
|
||||
let &ColumnSample {
|
||||
alt,
|
||||
chaos,
|
||||
@ -159,7 +160,7 @@ impl<'a> BlockGen<'a> {
|
||||
|
||||
chunk,
|
||||
..
|
||||
} = &z_cache?.sample;
|
||||
} = sample;
|
||||
|
||||
let structures = &z_cache?.structures;
|
||||
|
||||
@ -171,9 +172,12 @@ impl<'a> BlockGen<'a> {
|
||||
(true, alt, CONFIG.sea_level /*water_level*/)
|
||||
} else {
|
||||
// Apply warping
|
||||
let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(24.0)) as f32)
|
||||
let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(48.0)) as f32)
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(30.0);
|
||||
.mul(48.0)
|
||||
+ (world.sim().gen_ctx.warp_nz.get(wposf.div(15.0)) as f32)
|
||||
.mul((chaos - 0.1).max(0.0))
|
||||
.mul(15.0);
|
||||
|
||||
let height = if (wposf.z as f32) < alt + warp - 10.0 {
|
||||
// Shortcut cliffs
|
||||
@ -348,7 +352,7 @@ impl<'a> BlockGen<'a> {
|
||||
.structures
|
||||
.town
|
||||
.as_ref()
|
||||
.and_then(|town| TownGen.get((town, wpos)))
|
||||
.and_then(|town| TownGen.get((town, wpos, sample)))
|
||||
});
|
||||
|
||||
let block = structures
|
||||
@ -405,6 +409,19 @@ impl<'a> ZCache<'a> {
|
||||
.max(self.sample.water_level)
|
||||
.max(CONFIG.sea_level + 2.0);
|
||||
|
||||
// Structures
|
||||
let (min, max) = self
|
||||
.sample
|
||||
.chunk
|
||||
.structures
|
||||
.town
|
||||
.as_ref()
|
||||
.map(|town| {
|
||||
let (town_min, town_max) = TownGen.get_z_limits(town, self.wpos, &self.sample);
|
||||
(town_min.min(min), town_max.max(max))
|
||||
})
|
||||
.unwrap_or((min, max));
|
||||
|
||||
(min, max)
|
||||
}
|
||||
}
|
||||
|
@ -422,6 +422,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
|
||||
// Cities
|
||||
// TODO: In a later MR
|
||||
/*
|
||||
let building = match &sim_chunk.location {
|
||||
Some(loc) => {
|
||||
let loc = &sim.locations[loc.loc_idx];
|
||||
@ -440,6 +441,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
};
|
||||
|
||||
let alt = alt + building;
|
||||
*/
|
||||
|
||||
// Caves
|
||||
let cave_at = |wposf: Vec2<f64>| {
|
||||
|
@ -3,11 +3,12 @@ mod town;
|
||||
// Reexports
|
||||
pub use self::town::{TownGen, TownState};
|
||||
|
||||
use crate::util::Sampler;
|
||||
use crate::{column::ColumnSample, util::Sampler};
|
||||
use common::terrain::Block;
|
||||
use vek::*;
|
||||
|
||||
pub trait Generator<'a, T: 'a>:
|
||||
Sampler<'a, Index = (&'a T, Vec3<i32>), Sample = Option<Block>>
|
||||
Sampler<'a, Index = (&'a T, Vec3<i32>, &'a ColumnSample<'a>), Sample = Option<Block>>
|
||||
{
|
||||
fn get_z_limits(&self, state: &'a T, wpos: Vec2<i32>, sample: &ColumnSample) -> (f32, f32);
|
||||
}
|
||||
|
@ -1,24 +1,130 @@
|
||||
use super::Generator;
|
||||
use crate::util::Sampler;
|
||||
use crate::{
|
||||
column::ColumnSample,
|
||||
sim::WorldSim,
|
||||
util::{seed_expan, Grid, Sampler},
|
||||
};
|
||||
use common::terrain::{Block, BlockKind};
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use vek::*;
|
||||
|
||||
const CELL_SIZE: i32 = 24;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TownState;
|
||||
pub enum TownCell {
|
||||
Empty,
|
||||
Junction,
|
||||
Road,
|
||||
House,
|
||||
}
|
||||
|
||||
pub struct TownState {
|
||||
pub center: Vec2<i32>,
|
||||
pub radius: i32,
|
||||
pub grid: Grid<TownCell>,
|
||||
}
|
||||
|
||||
impl TownState {
|
||||
pub fn generate(center: Vec2<i32>, seed: u32, sim: &mut WorldSim) -> Option<Self> {
|
||||
let center_chunk = sim.get_wpos(center)?;
|
||||
|
||||
// First, determine whether the location is even appropriate for a town
|
||||
if center_chunk.chaos > 0.5 || center_chunk.near_cliffs {
|
||||
return None;
|
||||
}
|
||||
|
||||
let radius = 150;
|
||||
|
||||
let mut grid = Grid::new(TownCell::Empty, Vec2::broadcast(radius * 2 / CELL_SIZE));
|
||||
|
||||
grid.set(grid.size() / 2, TownCell::Junction);
|
||||
|
||||
let mut create_road = || loop {
|
||||
let junctions = grid
|
||||
.iter()
|
||||
.filter(|(_, cell)| {
|
||||
if let TownCell::Junction = cell {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Choose an existing junction for the road to start from
|
||||
let start_pos = junctions.choose(&mut sim.rng).unwrap().0; // Can't fail
|
||||
|
||||
// Choose a random direction and length for the road
|
||||
let road_dir = {
|
||||
let dirs = [-1, 0, 1, 0, -1];
|
||||
let idx = sim.rng.gen_range(0, 4);
|
||||
Vec2::new(dirs[idx], dirs[idx + 1])
|
||||
};
|
||||
let road_len = sim.rng.gen_range(1, 4) * 2 + 1;
|
||||
|
||||
// Make sure we aren't trying to create a road where a road already exists!
|
||||
match grid.get(start_pos + road_dir) {
|
||||
Some(TownCell::Empty) => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
// Pave the road
|
||||
for i in 1..road_len {
|
||||
let cell_pos = start_pos + road_dir * i;
|
||||
if let Some(TownCell::Empty) = grid.get(cell_pos) {
|
||||
grid.set(cell_pos, TownCell::Road);
|
||||
}
|
||||
}
|
||||
grid.set(start_pos + road_dir * road_len, TownCell::Junction);
|
||||
|
||||
break;
|
||||
};
|
||||
|
||||
for _ in 0..8 {
|
||||
create_road();
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
center,
|
||||
radius,
|
||||
grid,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_cell(&self, wpos: Vec2<i32>) -> &TownCell {
|
||||
self.grid
|
||||
.get((wpos - self.center + self.radius) / CELL_SIZE)
|
||||
.unwrap_or(&TownCell::Empty)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TownGen;
|
||||
|
||||
impl<'a> Sampler<'a> for TownGen {
|
||||
type Index = (&'a TownState, Vec3<i32>);
|
||||
type Index = (&'a TownState, Vec3<i32>, &'a ColumnSample<'a>);
|
||||
type Sample = Option<Block>;
|
||||
|
||||
fn get(&self, (town, pos): Self::Index) -> Self::Sample {
|
||||
if pos.z < 150 {
|
||||
Some(Block::new(BlockKind::Normal, Rgb::broadcast(255)))
|
||||
} else {
|
||||
None
|
||||
fn get(&self, (town, wpos, sample): Self::Index) -> Self::Sample {
|
||||
match town.get_cell(Vec2::from(wpos)) {
|
||||
TownCell::Road if wpos.z < sample.alt as i32 + 4 => {
|
||||
Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 150)))
|
||||
}
|
||||
TownCell::Junction if wpos.z < sample.alt as i32 + 4 => {
|
||||
Some(Block::new(BlockKind::Normal, Rgb::new(255, 200, 250)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Generator<'a, TownState> for TownGen {}
|
||||
impl<'a> Generator<'a, TownState> for TownGen {
|
||||
fn get_z_limits(
|
||||
&self,
|
||||
town: &'a TownState,
|
||||
wpos: Vec2<i32>,
|
||||
sample: &ColumnSample,
|
||||
) -> (f32, f32) {
|
||||
(sample.alt - 32.0, sample.alt + 64.0)
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ use noise::{
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaChaRng;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
f32,
|
||||
ops::{Add, Div, Mul, Neg, Sub},
|
||||
sync::Arc,
|
||||
@ -87,6 +88,8 @@ pub(crate) struct GenCtx {
|
||||
|
||||
pub fast_turb_x_nz: FastNoise,
|
||||
pub fast_turb_y_nz: FastNoise,
|
||||
|
||||
pub town_gen: StructureGen2d,
|
||||
}
|
||||
|
||||
pub struct WorldSim {
|
||||
@ -141,6 +144,8 @@ impl WorldSim {
|
||||
|
||||
fast_turb_x_nz: FastNoise::new(gen_seed()),
|
||||
fast_turb_y_nz: FastNoise::new(gen_seed()),
|
||||
|
||||
town_gen: StructureGen2d::new(gen_seed(), 1024, 512),
|
||||
};
|
||||
|
||||
// "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied value is
|
||||
@ -418,6 +423,38 @@ impl WorldSim {
|
||||
}
|
||||
}
|
||||
|
||||
// Stage 2 - towns!
|
||||
let mut maybe_towns = HashMap::new();
|
||||
for i in 0..WORLD_SIZE.x {
|
||||
for j in 0..WORLD_SIZE.y {
|
||||
let chunk_pos = Vec2::new(i as i32, j as i32);
|
||||
let wpos = chunk_pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
|
||||
e * sz as i32 + sz as i32 / 2
|
||||
});
|
||||
|
||||
let near_towns = self.gen_ctx.town_gen.get(wpos);
|
||||
let town = near_towns
|
||||
.iter()
|
||||
.min_by_key(|(pos, seed)| wpos.distance_squared(*pos));
|
||||
|
||||
if let Some((pos, seed)) = town {
|
||||
let maybe_town = maybe_towns
|
||||
.entry(*pos)
|
||||
.or_insert_with(|| {
|
||||
TownState::generate(*pos, *seed, self).map(|t| Arc::new(t))
|
||||
})
|
||||
.as_mut()
|
||||
// Only care if we're close to the town
|
||||
.filter(|town| {
|
||||
town.center.distance_squared(wpos) < town.radius.add(64).pow(2)
|
||||
})
|
||||
.cloned();
|
||||
|
||||
self.get_mut(chunk_pos).unwrap().structures.town = maybe_town;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.rng = rng;
|
||||
self.locations = locations;
|
||||
}
|
||||
@ -433,6 +470,12 @@ impl WorldSim {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_wpos(&self, wpos: Vec2<i32>) -> Option<&SimChunk> {
|
||||
self.get(wpos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
|
||||
e / sz as i32
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, chunk_pos: Vec2<i32>) -> Option<&mut SimChunk> {
|
||||
if chunk_pos
|
||||
.map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32)
|
||||
@ -610,7 +653,7 @@ impl SimChunk {
|
||||
.mul(1.3)
|
||||
.max(0.0),
|
||||
is_cliffs: cliff > 0.5 && alt > CONFIG.sea_level + 5.0,
|
||||
near_cliffs: cliff > 0.25,
|
||||
near_cliffs: cliff > 0.2,
|
||||
tree_density,
|
||||
forest_kind: if temp > 0.0 {
|
||||
if temp > CONFIG.desert_temp {
|
||||
|
46
world/src/util/grid.rs
Normal file
46
world/src/util/grid.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use vek::*;
|
||||
|
||||
pub struct Grid<T> {
|
||||
cells: Vec<T>,
|
||||
size: Vec2<i32>,
|
||||
}
|
||||
|
||||
impl<T: Clone> Grid<T> {
|
||||
pub fn new(default_cell: T, size: Vec2<i32>) -> Self {
|
||||
Self {
|
||||
cells: vec![default_cell; size.product() as usize],
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
fn idx(&self, pos: Vec2<i32>) -> usize {
|
||||
(pos.y * self.size.x + pos.x) as usize
|
||||
}
|
||||
|
||||
pub fn size(&self) -> Vec2<i32> {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn get(&self, pos: Vec2<i32>) -> Option<&T> {
|
||||
self.cells.get(self.idx(pos))
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, pos: Vec2<i32>) -> Option<&mut T> {
|
||||
let idx = self.idx(pos);
|
||||
self.cells.get_mut(idx)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, pos: Vec2<i32>, cell: T) {
|
||||
let idx = self.idx(pos);
|
||||
self.cells.get_mut(idx).map(|c| *c = cell);
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &T)> + '_ {
|
||||
(0..self.size.x)
|
||||
.map(move |x| {
|
||||
(0..self.size.y)
|
||||
.map(move |y| (Vec2::new(x, y), &self.cells[self.idx(Vec2::new(x, y))]))
|
||||
})
|
||||
.flatten()
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
pub mod fast_noise;
|
||||
pub mod grid;
|
||||
pub mod hash_cache;
|
||||
pub mod random;
|
||||
pub mod sampler;
|
||||
@ -9,6 +10,7 @@ pub mod unit_chooser;
|
||||
// Reexports
|
||||
pub use self::{
|
||||
fast_noise::FastNoise,
|
||||
grid::Grid,
|
||||
hash_cache::HashCache,
|
||||
random::{RandomField, RandomPerm},
|
||||
sampler::{Sampler, SamplerMut},
|
||||
|
Loading…
Reference in New Issue
Block a user