2019-08-19 12:39:23 +00:00
|
|
|
#![deny(unsafe_code)]
|
2019-09-06 10:52:47 +00:00
|
|
|
#![allow(incomplete_features)]
|
2020-01-18 18:41:37 +00:00
|
|
|
#![feature(arbitrary_enum_discriminant, const_generics, label_break_value)]
|
2019-05-23 20:41:01 +00:00
|
|
|
|
2019-06-11 18:39:25 +00:00
|
|
|
mod all;
|
2019-06-09 10:24:18 +00:00
|
|
|
mod block;
|
2020-04-17 23:29:01 +00:00
|
|
|
pub mod civ;
|
2019-06-09 10:24:18 +00:00
|
|
|
mod column;
|
2019-06-15 12:34:28 +00:00
|
|
|
pub mod config;
|
2019-06-18 21:22:31 +00:00
|
|
|
pub mod sim;
|
2020-02-26 21:43:31 +00:00
|
|
|
pub mod site;
|
2019-06-15 12:34:28 +00:00
|
|
|
pub mod util;
|
2019-06-09 10:24:18 +00:00
|
|
|
|
|
|
|
// Reexports
|
|
|
|
pub use crate::config::CONFIG;
|
2019-05-18 08:59:58 +00:00
|
|
|
|
2019-06-15 10:36:26 +00:00
|
|
|
use crate::{
|
|
|
|
block::BlockGen,
|
2019-06-15 12:34:28 +00:00
|
|
|
column::{ColumnGen, ColumnSample},
|
2020-02-07 22:19:25 +00:00
|
|
|
util::{Grid, Sampler},
|
2019-06-15 10:36:26 +00:00
|
|
|
};
|
2019-01-15 15:13:11 +00:00
|
|
|
use common::{
|
2020-04-17 20:58:36 +00:00
|
|
|
generation::{ChunkSupplement, EntityInfo},
|
|
|
|
comp::{Alignment},
|
2019-08-14 21:28:37 +00:00
|
|
|
terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
2019-01-15 15:13:11 +00:00
|
|
|
};
|
2019-08-02 14:37:26 +00:00
|
|
|
use rand::Rng;
|
2019-09-16 13:11:47 +00:00
|
|
|
use std::time::Duration;
|
2019-05-21 22:31:38 +00:00
|
|
|
use vek::*;
|
2019-01-15 15:13:11 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
Other(String),
|
|
|
|
}
|
|
|
|
|
2019-05-18 08:59:58 +00:00
|
|
|
pub struct World {
|
|
|
|
sim: sim::WorldSim,
|
2020-03-27 13:16:02 +00:00
|
|
|
civs: civ::Civs,
|
2019-05-18 08:59:58 +00:00
|
|
|
}
|
2019-01-15 15:13:11 +00:00
|
|
|
|
|
|
|
impl World {
|
2019-12-03 01:07:44 +00:00
|
|
|
pub fn generate(seed: u32, opts: sim::WorldOpts) -> Self {
|
2020-03-27 13:16:02 +00:00
|
|
|
let mut sim = sim::WorldSim::generate(seed, opts);
|
|
|
|
let civs = civ::Civs::generate(seed, &mut sim);
|
|
|
|
Self { sim, civs }
|
2019-05-18 08:59:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-01 20:39:39 +00:00
|
|
|
pub fn sim(&self) -> &sim::WorldSim { &self.sim }
|
2019-05-21 00:57:16 +00:00
|
|
|
|
2020-03-27 23:06:23 +00:00
|
|
|
pub fn civs(&self) -> &civ::Civs { &self.civs }
|
|
|
|
|
2019-07-01 18:40:41 +00:00
|
|
|
pub fn tick(&self, _dt: Duration) {
|
2019-05-18 08:59:58 +00:00
|
|
|
// TODO
|
2019-01-15 15:13:11 +00:00
|
|
|
}
|
|
|
|
|
2019-06-15 12:34:28 +00:00
|
|
|
pub fn sample_columns(
|
|
|
|
&self,
|
|
|
|
) -> impl Sampler<Index = Vec2<i32>, Sample = Option<ColumnSample>> + '_ {
|
2019-08-24 22:57:55 +00:00
|
|
|
ColumnGen::new(&self.sim)
|
2019-06-15 12:34:28 +00:00
|
|
|
}
|
|
|
|
|
2020-02-01 20:39:39 +00:00
|
|
|
pub fn sample_blocks(&self) -> BlockGen { BlockGen::new(ColumnGen::new(&self.sim)) }
|
2019-01-23 22:21:47 +00:00
|
|
|
|
2019-09-16 02:01:05 +00:00
|
|
|
pub fn generate_chunk(
|
|
|
|
&self,
|
|
|
|
chunk_pos: Vec2<i32>,
|
2020-01-09 06:05:20 +00:00
|
|
|
// TODO: misleading name
|
2019-09-16 13:11:47 +00:00
|
|
|
mut should_continue: impl FnMut() -> bool,
|
2019-09-16 02:05:36 +00:00
|
|
|
) -> Result<(TerrainChunk, ChunkSupplement), ()> {
|
2020-04-16 01:40:09 +00:00
|
|
|
let mut sampler = self.sample_blocks();
|
|
|
|
|
|
|
|
let chunk_wpos2d = Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
|
|
|
|
let grid_border = 4;
|
2020-04-17 23:29:01 +00:00
|
|
|
let zcache_grid = Grid::populate_from(
|
|
|
|
TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2,
|
|
|
|
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs),
|
|
|
|
);
|
2020-04-16 01:40:09 +00:00
|
|
|
|
2019-01-23 20:01:58 +00:00
|
|
|
let air = Block::empty();
|
2020-04-17 23:29:01 +00:00
|
|
|
let stone = Block::new(
|
|
|
|
BlockKind::Dense,
|
|
|
|
zcache_grid
|
|
|
|
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
|
|
|
|
.and_then(|zcache| zcache.as_ref())
|
|
|
|
.map(|zcache| zcache.sample.stone_col)
|
|
|
|
.unwrap_or(Rgb::new(125, 120, 130)),
|
|
|
|
);
|
2019-08-16 14:58:14 +00:00
|
|
|
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
|
2019-01-23 20:01:58 +00:00
|
|
|
|
2019-10-16 11:39:41 +00:00
|
|
|
let _chunk_size2d = TerrainChunkSize::RECT_SIZE;
|
2019-06-19 14:55:26 +00:00
|
|
|
let (base_z, sim_chunk) = match self
|
|
|
|
.sim
|
2019-10-16 11:39:41 +00:00
|
|
|
/*.get_interpolated(
|
2019-06-19 14:55:26 +00:00
|
|
|
chunk_pos.map2(chunk_size2d, |e, sz: u32| e * sz as i32 + sz as i32 / 2),
|
|
|
|
|chunk| chunk.get_base_z(),
|
|
|
|
)
|
2019-10-16 11:39:41 +00:00
|
|
|
.and_then(|base_z| self.sim.get(chunk_pos).map(|sim_chunk| (base_z, sim_chunk))) */
|
|
|
|
.get_base_z(chunk_pos)
|
2019-06-19 14:55:26 +00:00
|
|
|
{
|
2019-10-16 11:39:41 +00:00
|
|
|
Some(base_z) => (base_z as i32, self.sim.get(chunk_pos).unwrap()),
|
|
|
|
// Some((base_z, sim_chunk)) => (base_z as i32, sim_chunk),
|
2019-07-09 20:42:27 +00:00
|
|
|
None => {
|
2019-09-16 01:51:08 +00:00
|
|
|
return Ok((
|
2019-08-02 14:37:26 +00:00
|
|
|
TerrainChunk::new(
|
|
|
|
CONFIG.sea_level as i32,
|
|
|
|
water,
|
|
|
|
air,
|
|
|
|
TerrainChunkMeta::void(),
|
|
|
|
),
|
|
|
|
ChunkSupplement::default(),
|
2020-02-01 20:39:39 +00:00
|
|
|
));
|
|
|
|
},
|
2019-05-19 14:54:37 +00:00
|
|
|
};
|
2019-05-17 21:19:32 +00:00
|
|
|
|
2019-06-25 15:59:09 +00:00
|
|
|
let meta = TerrainChunkMeta::new(sim_chunk.get_name(&self.sim), sim_chunk.get_biome());
|
2019-08-02 14:37:26 +00:00
|
|
|
|
|
|
|
let mut chunk = TerrainChunk::new(base_z, stone, air, meta);
|
2020-01-13 04:12:56 +00:00
|
|
|
for y in 0..TerrainChunkSize::RECT_SIZE.y as i32 {
|
|
|
|
for x in 0..TerrainChunkSize::RECT_SIZE.x as i32 {
|
2019-09-16 13:11:47 +00:00
|
|
|
if should_continue() {
|
2019-09-16 02:05:36 +00:00
|
|
|
return Err(());
|
2019-09-16 02:01:05 +00:00
|
|
|
};
|
2019-05-20 02:53:04 +00:00
|
|
|
|
2020-02-07 22:19:25 +00:00
|
|
|
let offs = Vec2::new(x, y);
|
|
|
|
let wpos2d = chunk_wpos2d + offs;
|
|
|
|
|
2020-04-16 01:40:09 +00:00
|
|
|
let z_cache = match zcache_grid.get(grid_border + offs) {
|
2020-02-07 22:19:25 +00:00
|
|
|
Some(Some(z_cache)) => z_cache,
|
|
|
|
_ => continue,
|
2019-07-08 14:51:38 +00:00
|
|
|
};
|
2019-07-04 23:14:55 +00:00
|
|
|
|
2019-09-23 15:20:28 +00:00
|
|
|
let (min_z, only_structures_min_z, max_z) = z_cache.get_z_limits(&mut sampler);
|
2019-07-08 14:51:38 +00:00
|
|
|
|
2020-01-13 04:12:56 +00:00
|
|
|
(base_z..min_z as i32).for_each(|z| {
|
2019-07-08 14:51:38 +00:00
|
|
|
let _ = chunk.set(Vec3::new(x, y, z), stone);
|
2020-01-13 04:12:56 +00:00
|
|
|
});
|
2019-07-08 14:51:38 +00:00
|
|
|
|
2020-01-13 04:12:56 +00:00
|
|
|
(min_z as i32..max_z as i32).for_each(|z| {
|
2019-05-17 17:44:30 +00:00
|
|
|
let lpos = Vec3::new(x, y, z);
|
2020-02-07 22:19:25 +00:00
|
|
|
let wpos = Vec3::from(chunk_wpos2d) + lpos;
|
2019-09-23 15:20:28 +00:00
|
|
|
let only_structures = lpos.z >= only_structures_min_z as i32;
|
2019-05-24 11:08:38 +00:00
|
|
|
|
2019-09-23 15:20:28 +00:00
|
|
|
if let Some(block) =
|
|
|
|
sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures)
|
|
|
|
{
|
2019-06-09 10:24:18 +00:00
|
|
|
let _ = chunk.set(lpos, block);
|
|
|
|
}
|
2020-01-13 04:12:56 +00:00
|
|
|
});
|
2019-05-17 17:44:30 +00:00
|
|
|
}
|
2019-01-23 20:01:58 +00:00
|
|
|
}
|
|
|
|
|
2020-02-26 22:44:30 +00:00
|
|
|
// Apply site generation
|
|
|
|
sim_chunk.sites.iter().for_each(|site| {
|
|
|
|
site.apply_to(
|
|
|
|
chunk_wpos2d,
|
|
|
|
|offs| {
|
|
|
|
zcache_grid
|
2020-04-11 15:50:40 +00:00
|
|
|
.get(grid_border + offs)
|
2020-02-26 22:44:30 +00:00
|
|
|
.map(Option::as_ref)
|
|
|
|
.flatten()
|
|
|
|
.map(|zc| &zc.sample)
|
|
|
|
},
|
|
|
|
&mut chunk,
|
|
|
|
)
|
|
|
|
});
|
2020-02-08 11:38:44 +00:00
|
|
|
|
2019-08-02 14:37:26 +00:00
|
|
|
let gen_entity_pos = || {
|
common: Rework volume API
See the doc comments in `common/src/vol.rs` for more information on
the API itself.
The changes include:
* Consistent `Err`/`Error` naming.
* Types are named `...Error`.
* `enum` variants are named `...Err`.
* Rename `VolMap{2d, 3d}` -> `VolGrid{2d, 3d}`. This is in preparation
to an upcoming change where a “map” in the game related sense will
be added.
* Add volume iterators. There are two types of them:
* _Position_ iterators obtained from the trait `IntoPosIterator`
using the method
`fn pos_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `Vec3<i32>`.
* _Volume_ iterators obtained from the trait `IntoVolIterator`
using the method
`fn vol_iter(self, lower_bound: Vec3<i32>, upper_bound: Vec3<i32>) -> ...`
which returns an iterator over `(Vec3<i32>, &Self::Vox)`.
Those traits will usually be implemented by references to volume
types (i.e. `impl IntoVolIterator<'a> for &'a T` where `T` is some
type which usually implements several volume traits, such as `Chunk`).
* _Position_ iterators iterate over the positions valid for that
volume.
* _Volume_ iterators do the same but return not only the position
but also the voxel at that position, in each iteration.
* Introduce trait `RectSizedVol` for the use case which we have with
`Chonk`: A `Chonk` is sized only in x and y direction.
* Introduce traits `RasterableVol`, `RectRasterableVol`
* `RasterableVol` represents a volume that is compile-time sized and has
its lower bound at `(0, 0, 0)`. The name `RasterableVol` was chosen
because such a volume can be used with `VolGrid3d`.
* `RectRasterableVol` represents a volume that is compile-time sized at
least in x and y direction and has its lower bound at `(0, 0, z)`.
There's no requirement on he lower bound or size in z direction.
The name `RectRasterableVol` was chosen because such a volume can be
used with `VolGrid2d`.
2019-09-03 22:23:29 +00:00
|
|
|
let lpos2d = TerrainChunkSize::RECT_SIZE
|
2019-08-02 14:37:26 +00:00
|
|
|
.map(|sz| rand::thread_rng().gen::<u32>().rem_euclid(sz));
|
|
|
|
let mut lpos = Vec3::new(lpos2d.x as i32, lpos2d.y as i32, 0);
|
|
|
|
|
|
|
|
while chunk.get(lpos).map(|vox| !vox.is_empty()).unwrap_or(false) {
|
|
|
|
lpos.z += 1;
|
|
|
|
}
|
|
|
|
|
2020-02-07 22:19:25 +00:00
|
|
|
(Vec3::from(chunk_wpos2d) + lpos).map(|e: i32| e as f32) + 0.5
|
2019-08-02 14:37:26 +00:00
|
|
|
};
|
|
|
|
|
2020-04-17 20:58:36 +00:00
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
|
2019-08-02 14:37:26 +00:00
|
|
|
const SPAWN_RATE: f32 = 0.1;
|
2019-08-04 09:27:08 +00:00
|
|
|
const BOSS_RATE: f32 = 0.03;
|
2020-01-25 12:27:36 +00:00
|
|
|
let mut supplement = ChunkSupplement {
|
2020-04-17 20:58:36 +00:00
|
|
|
entities: if rng.gen::<f32>() < SPAWN_RATE
|
2020-01-08 12:48:00 +00:00
|
|
|
&& sim_chunk.chaos < 0.5
|
2020-01-26 15:18:51 +00:00
|
|
|
&& !sim_chunk.is_underwater()
|
2020-01-08 12:48:00 +00:00
|
|
|
{
|
2020-04-17 20:58:36 +00:00
|
|
|
let entity = EntityInfo::at(gen_entity_pos())
|
|
|
|
.with_alignment(Alignment::Wild)
|
|
|
|
.do_if(rng.gen(), |e| e.into_giant());
|
|
|
|
|
|
|
|
vec![entity]
|
2019-08-02 14:37:26 +00:00
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-01-25 20:43:34 +00:00
|
|
|
if sim_chunk.contains_waypoint {
|
2020-04-17 20:58:36 +00:00
|
|
|
supplement.add_entity(EntityInfo::at(gen_entity_pos())
|
|
|
|
.into_waypoint());
|
2020-01-25 12:27:36 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 21:01:13 +00:00
|
|
|
// Apply site supplementary information
|
|
|
|
sim_chunk.sites.iter().for_each(|site| {
|
|
|
|
site.apply_supplement(
|
|
|
|
chunk_wpos2d,
|
|
|
|
|offs| {
|
|
|
|
zcache_grid
|
|
|
|
.get(grid_border + offs)
|
|
|
|
.map(Option::as_ref)
|
|
|
|
.flatten()
|
|
|
|
.map(|zc| &zc.sample)
|
|
|
|
},
|
|
|
|
&mut supplement,
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
2019-09-16 01:51:08 +00:00
|
|
|
Ok((chunk, supplement))
|
2019-08-02 14:37:26 +00:00
|
|
|
}
|
|
|
|
}
|