mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added monsters to caves
This commit is contained in:
parent
d7ccb28ea7
commit
9329b4ce55
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
20
world/src/site/castle/keep.rs
Normal file
20
world/src/site/castle/keep.rs
Normal 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,
|
||||
}
|
@ -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,
|
||||
|
@ -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
26
world/src/util/wgrid.rs
Normal 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())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user