Added monsters to caves

This commit is contained in:
Joshua Barretto 2020-08-07 17:47:50 +01:00
parent d7ccb28ea7
commit 9329b4ce55
8 changed files with 230 additions and 27 deletions

View File

@ -122,11 +122,9 @@ impl<S: Clone + Eq + Hash, H: BuildHasher + Clone> Astar<S, H> {
let iter_limit = self.max_iters.min(self.iter + iters);
while self.iter < iter_limit {
if let Some(PathEntry { node, cost }) = self.potential_nodes.pop() {
self.cheapest_cost = Some(cost);
if satisfied(&node) {
return PathResult::Path(self.reconstruct_path_to(node));
} else {
self.cheapest_node = Some(node.clone());
for neighbor in neighbors(&node) {
let node_cheapest = self.cheapest_scores.get(&node).unwrap_or(&f32::MAX);
let neighbor_cheapest =
@ -136,12 +134,18 @@ impl<S: Clone + Eq + Hash, H: BuildHasher + Clone> Astar<S, H> {
if cost < *neighbor_cheapest {
self.came_from.insert(neighbor.clone(), node.clone());
self.cheapest_scores.insert(neighbor.clone(), cost);
let neighbor_cost = cost + heuristic(&neighbor);
let h = heuristic(&neighbor);
let neighbor_cost = cost + h;
self.final_scores.insert(neighbor.clone(), neighbor_cost);
if self.cheapest_cost.map(|cc| h < cc).unwrap_or(true) {
self.cheapest_node = Some(node.clone());
self.cheapest_cost = Some(h);
};
if self.visited.insert(neighbor.clone()) {
self.potential_nodes.push(PathEntry {
node: neighbor.clone(),
node: neighbor,
cost: neighbor_cost,
});
}

View File

@ -99,6 +99,11 @@ impl Route {
V: BaseVol<Vox = Block> + ReadVol,
{
let (next0, next1, next_tgt, be_precise) = loop {
// If we've reached the end of the path, stop
if self.next(0).is_none() {
return None;
}
let next0 = self
.next(0)
.unwrap_or_else(|| pos.map(|e| e.floor() as i32));
@ -110,7 +115,7 @@ impl Route {
}
let be_precise = DIAGONALS.iter().any(|pos| {
(-2..2)
(-1..2)
.all(|z| vol.get(next0 + Vec3::new(pos.x, pos.y, z))
.map(|b| !b.is_solid())
.unwrap_or(false))
@ -312,7 +317,7 @@ impl Route {
#[derive(Default, Clone, Debug)]
pub struct Chaser {
last_search_tgt: Option<Vec3<f32>>,
route: Option<Route>,
route: Option<(Route, bool)>,
/// We use this hasher (AAHasher) because:
/// (1) we care about DDOS attacks (ruling out FxHash);
/// (2) we don't care about determinism across computers (we can use
@ -335,14 +340,22 @@ impl Chaser {
let pos_to_tgt = pos.distance(tgt);
// If we're already close to the target then there's nothing to do
if ((pos - tgt) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared()
let end = self.route
.as_ref()
.and_then(|(r, _)| r.path.end().copied())
.map(|e| e.map(|e| e as f32 + 0.5))
.unwrap_or(tgt);
if ((pos - end) * Vec3::new(1.0, 1.0, 2.0)).magnitude_squared()
< traversal_cfg.min_tgt_dist.powf(2.0)
{
self.route = None;
return None;
}
let bearing = if let Some(end) = self.route.as_ref().and_then(|r| r.path().end().copied()) {
let bearing = if let Some((end, complete)) = self.route
.as_ref()
.and_then(|(r, complete)| Some((r.path().end().copied()?, *complete)))
{
let end_to_tgt = end.map(|e| e as f32).distance(tgt);
// If the target has moved significantly since the path was generated then it's
// time to search for a new path. Also, do this randomly from time
@ -350,12 +363,12 @@ impl Chaser {
// theory this shouldn't happen, but in practice the world is full
// of unpredictable obstacles that are more than willing to mess up
// our day. TODO: Come up with a better heuristic for this
if end_to_tgt > pos_to_tgt * 0.3 + 5.0 || thread_rng().gen::<f32>() < 0.001 {
if (end_to_tgt > pos_to_tgt * 0.3 + 5.0 && complete) || thread_rng().gen::<f32>() < 0.001 {
None
} else {
self.route
.as_mut()
.and_then(|r| r.traverse(vol, pos, vel, traversal_cfg))
.and_then(|(r, _)| r.traverse(vol, pos, vel, traversal_cfg))
}
} else {
None
@ -376,7 +389,9 @@ impl Chaser {
|| self.astar.is_some()
|| self.route.is_none()
{
let (start_pos, path) = find_path(&mut self.astar, vol, pos, tgt);
self.last_search_tgt = Some(tgt);
let (path, complete) = find_path(&mut self.astar, vol, pos, tgt);
self.route = path.map(|path| {
let start_index = path
@ -385,10 +400,10 @@ impl Chaser {
.min_by_key(|(_, node)| node.xy().map(|e| e as f32).distance_squared(pos.xy() + tgt_dir) as i32)
.map(|(idx, _)| idx);
Route {
(Route {
path,
next_idx: start_index.unwrap_or(0),
}
}, complete)
});
}
@ -424,13 +439,14 @@ where
.unwrap_or(true)
}
#[allow(clippy::float_cmp)] // TODO: Pending review in #587
/// Attempt to search for a path to a target, returning the path (if one was found)
/// and whether it is complete (reaches the target)
fn find_path<V>(
astar: &mut Option<Astar<Vec3<i32>, DefaultHashBuilder>>,
vol: &V,
startf: Vec3<f32>,
endf: Vec3<f32>,
) -> (Vec3<f32>, Option<Path<Vec3<i32>>>)
) -> (Option<Path<Vec3<i32>>>, bool)
where
V: BaseVol<Vox = Block> + ReadVol,
{
@ -452,7 +468,7 @@ where
get_walkable_z(endf.map(|e| e.floor() as i32)),
) {
(Some(start), Some(end)) => (start, end),
_ => return (startf, None),
_ => return (None, false),
};
let heuristic = |pos: &Vec3<i32>| (pos.distance_squared(end) as f32).sqrt();
@ -554,19 +570,19 @@ where
*astar = Some(new_astar);
(startf, match path_result {
match path_result {
PathResult::Path(path) => {
*astar = None;
Some(path)
(Some(path), true)
},
PathResult::None(path) => {
*astar = None;
Some(path)
(Some(path), false)
},
PathResult::Exhausted(path) => {
*astar = None;
Some(path)
(Some(path), false)
},
PathResult::Pending => None,
})
PathResult::Pending => (None, false),
}
}

View File

@ -5,9 +5,11 @@ use crate::{
};
use common::{
assets,
comp,
lottery::Lottery,
terrain::{Block, BlockKind},
vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol},
generation::{ChunkSupplement, EntityInfo},
};
use noise::NoiseFn;
use std::{
@ -15,6 +17,7 @@ use std::{
ops::{Mul, Sub},
};
use vek::*;
use rand::prelude::*;
pub fn apply_paths_to<'a>(
wpos2d: Vec2<i32>,
@ -186,3 +189,102 @@ pub fn apply_caves_to<'a>(
}
}
}
pub fn apply_caves_supplement<'a>(
rng: &mut impl Rng,
wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
index: &Index,
supplement: &mut ChunkSupplement,
) {
for y in 0..vol.size_xy().y as i32 {
for x in 0..vol.size_xy().x as i32 {
let offs = Vec2::new(x, y);
let wpos2d = wpos2d + offs;
// Sample terrain
let col_sample = if let Some(col_sample) = get_column(offs) {
col_sample
} else {
continue;
};
let surface_z = col_sample.riverless_alt.floor() as i32;
if let Some((cave_dist, cave_nearest, cave, _)) = col_sample
.cave
.filter(|(dist, _, cave, _)| *dist < cave.width)
{
let cave_x = (cave_dist / cave.width).min(1.0);
// Relative units
let cave_floor = 0.0 - 0.5 * (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width;
let cave_height = (1.0 - cave_x.powf(2.0)).max(0.0).sqrt() * cave.width;
// Abs units
let cave_base = (cave.alt + cave_floor) as i32;
let cave_roof = (cave.alt + cave_height) as i32;
// Scatter things in caves
if RandomField::new(index.seed).chance(wpos2d.into(), 0.0001)
&& cave_base < surface_z as i32 - 40
{
let entity = EntityInfo::at(Vec3::new(wpos2d.x as f32, wpos2d.y as f32, cave_base as f32))
.with_alignment(comp::Alignment::Enemy)
.with_body(match rng.gen_range(0, 6) {
0 => {
let species = match rng.gen_range(0, 2) {
0 => comp::quadruped_small::Species::Truffler,
_ => comp::quadruped_small::Species::Hyena,
};
comp::quadruped_small::Body::random_with(rng, &species).into()
},
1 => {
let species = match rng.gen_range(0, 3) {
0 => comp::quadruped_medium::Species::Tarasque,
1 => comp::quadruped_medium::Species::Frostfang,
_ => comp::quadruped_medium::Species::Bonerattler,
};
comp::quadruped_medium::Body::random_with(rng, &species).into()
},
2 => {
let species = match rng.gen_range(0, 3) {
0 => comp::quadruped_low::Species::Maneater,
1 => comp::quadruped_low::Species::Rocksnapper,
_ => comp::quadruped_low::Species::Salamander,
};
comp::quadruped_low::Body::random_with(rng, &species).into()
},
3 => {
let species = match rng.gen_range(0, 3) {
0 => comp::critter::Species::Fungome,
1 => comp::critter::Species::Axolotl,
_ => comp::critter::Species::Rat,
};
comp::critter::Body::random_with(rng, &species).into()
},
4 => {
let species = match rng.gen_range(0, 1) {
_ => comp::golem::Species::StoneGolem,
};
comp::golem::Body::random_with(rng, &species).into()
},
_ => {
let species = match rng.gen_range(0, 4) {
0 => comp::biped_large::Species::Ogre,
1 => comp::biped_large::Species::Cyclops,
2 => comp::biped_large::Species::Wendigo,
_ => comp::biped_large::Species::Troll,
};
comp::biped_large::Body::random_with(rng, &species).into()
},
})
.with_automatic_name();
supplement.add_entity(entity);
}
}
}
}
}

View File

@ -228,6 +228,9 @@ impl World {
supplement.add_entity(EntityInfo::at(gen_entity_pos()).into_waypoint());
}
// Apply layer supplement
layer::apply_caves_supplement(&mut rng, chunk_wpos2d, sample_get, &chunk, &self.index, &mut supplement);
// Apply site supplementary information
sim_chunk.sites.iter().for_each(|site| {
self.index.sites[*site].apply_supplement(

View File

@ -0,0 +1,20 @@
use vek::*;
use crate::{
util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS},
};
pub struct Keep {
offset: Vec2<i32>,
cols: Grid<KeepCol>,
}
const KEEP_CELL_STOREY: i32 = 12;
pub struct KeepCol {
z_offset: i32,
storeys: Vec<KeepCell>,
}
enum KeepCell {
Cube,
}

View File

@ -1,3 +1,5 @@
mod keep;
use super::SpawnRules;
use crate::{
block::block_from_structure,
@ -52,6 +54,11 @@ pub struct Castle {
keeps: Vec<Keep>,
rounded_towers: bool,
ridged: bool,
flags: bool,
evil: bool,
keep: Option<keep::Keep>,
}
pub struct GenCtx<'a, R: Rng> {
@ -70,7 +77,7 @@ impl Castle {
let radius = 150;
let this = Self {
let mut this = Self {
origin: wpos,
alt: ctx
.sim
@ -116,6 +123,8 @@ impl Castle {
.collect(),
rounded_towers: ctx.rng.gen(),
ridged: ctx.rng.gen(),
flags: ctx.rng.gen(),
evil: ctx.rng.gen(),
keeps: (0..keep_count)
.map(|i| {
let angle = (i as f32 / keep_count as f32) * f32::consts::PI * 2.0;
@ -141,6 +150,8 @@ impl Castle {
}
})
.collect(),
keep: None,
};
this
@ -283,7 +294,16 @@ impl Castle {
let wall_alt = wall_alt + (wall_sample.alt as i32 - wall_alt - 10).max(0);
let keep_archetype = KeepArchetype {
flag_color: Rgb::new(200, 80, 40),
flag_color: if self.evil {
Rgb::new(80, 10, 130)
} else {
Rgb::new(200, 80, 40)
},
stone_color: if self.evil {
Rgb::new(65, 60, 55)
} else {
Rgb::new(100, 100, 110)
},
};
for z in -10..64 {
@ -307,6 +327,7 @@ impl Castle {
&Attr {
storeys: 2,
is_tower: false,
flag: self.flags,
ridged: false,
rounded: true,
has_doors: false,
@ -347,6 +368,7 @@ impl Castle {
&Attr {
storeys: 3,
is_tower: true,
flag: self.flags,
ridged: self.ridged,
rounded: self.rounded_towers,
has_doors: false,
@ -387,6 +409,7 @@ impl Castle {
&Attr {
storeys: keep.storeys,
is_tower: keep.is_tower,
flag: self.flags,
ridged: self.ridged,
rounded: self.rounded_towers,
has_doors: true,

View File

@ -12,11 +12,13 @@ use vek::*;
pub struct Keep {
pub flag_color: Rgb<u8>,
pub stone_color: Rgb<u8>,
}
pub struct Attr {
pub storeys: i32,
pub is_tower: bool,
pub flag: bool,
pub ridged: bool,
pub rounded: bool,
pub has_doors: bool,
@ -36,6 +38,7 @@ impl Archetype for Keep {
attr: Attr {
storeys,
is_tower: false,
flag: false,
ridged: false,
rounded: true,
has_doors: true,
@ -51,6 +54,7 @@ impl Archetype for Keep {
attr: Attr {
storeys: storeys + rng.gen_range(1, 3),
is_tower: true,
flag: true,
ridged: false,
rounded: true,
has_doors: false,
@ -68,6 +72,7 @@ impl Archetype for Keep {
(
Self {
flag_color: Rgb::new(200, 80, 40),
stone_color: Rgb::new(100, 100, 110),
},
skel,
)
@ -114,7 +119,11 @@ impl Archetype for Keep {
let brick_tex_pos = (pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1);
let brick_tex = RandomField::new(0).get(brick_tex_pos) as u8 % 24;
let foundation = make_block(80 + brick_tex, 80 + brick_tex, 80 + brick_tex);
let wall = make_block(100 + brick_tex, 100 + brick_tex, 110 + brick_tex);
let wall = make_block(
self.stone_color.r + brick_tex,
self.stone_color.g + brick_tex,
self.stone_color.b + brick_tex,
);
let window = BlockMask::new(
Block::new(BlockKind::Window1, make_meta(ori.flip())),
normal_layer,
@ -199,9 +208,9 @@ impl Archetype for Keep {
if profile.y > roof_height
&& (min_dist < rampart_width - 1 || (attr.is_tower && min_dist < rampart_width))
{
if attr.is_tower && center_offset == Vec2::zero() && profile.y < roof_height + 16 {
if attr.is_tower && attr.flag && center_offset == Vec2::zero() && profile.y < roof_height + 16 {
pole
} else if attr.is_tower
} else if attr.is_tower && attr.flag
&& center_offset.x == 0
&& center_offset.y > 0
&& center_offset.y < 8

26
world/src/util/wgrid.rs Normal file
View File

@ -0,0 +1,26 @@
use super::Grid;
use vek::*;
pub struct WGrid<T> {
cell_size: u32,
grid: Grid<T>,
}
impl<T> WGrid<T> {
pub fn new(radius: u32, cell_size: u32, default_cell: T) -> Self
where T: Clone
{
Self {
cell_size,
grid: Grid::new(Vec2::broadcast(radius as i32 * 2 + 1), default_cell),
}
}
fn offset(&self) -> Vec2<i32> {
self.grid.size() / 2
}
pub fn get_local(&self, pos: Vec2<i32>) -> Option<&T> {
self.grid.get(pos + self.offset())
}
}