mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added test world, friendly NPC pathfinding
This commit is contained in:
parent
97e7b1eabe
commit
2d9aa21eef
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -2329,6 +2329,7 @@ dependencies = [
|
||||
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2435,6 +2436,14 @@ dependencies = [
|
||||
"rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
@ -3858,6 +3867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
"checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a"
|
||||
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
"checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
"checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff"
|
||||
"checksum raw-window-handle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9db80d08d3ed847ce4fb3def46de0af4bfb6155bd09bd6eaf28b5ac72541c1f1"
|
||||
|
@ -80,6 +80,7 @@ where
|
||||
let mut visited = HashSet::new();
|
||||
visited.insert(initial.clone());
|
||||
|
||||
let mut iters = 0;
|
||||
while let Some(PathEntry { node: current, .. }) = potential_nodes.pop() {
|
||||
if current == target {
|
||||
return Some(reconstruct_path(&came_from, ¤t));
|
||||
@ -105,6 +106,12 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iters += 1;
|
||||
if iters >= 10000 {
|
||||
println!("Ran out of turns!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::pathfinding::WorldPath;
|
||||
use crate::{path::Chaser, pathfinding::WorldPath};
|
||||
use specs::{Component, Entity as EcsEntity};
|
||||
use specs_idvs::IDVStorage;
|
||||
use vek::*;
|
||||
@ -8,7 +8,7 @@ pub enum Agent {
|
||||
Wanderer(Vec2<f32>),
|
||||
Pet {
|
||||
target: EcsEntity,
|
||||
offset: Vec2<f32>,
|
||||
chaser: Chaser,
|
||||
},
|
||||
Enemy {
|
||||
bearing: Vec2<f32>,
|
||||
@ -26,6 +26,13 @@ impl Agent {
|
||||
target: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pet(target: EcsEntity) -> Self {
|
||||
Agent::Pet {
|
||||
target,
|
||||
chaser: Chaser::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Agent {
|
||||
|
11
common/src/generation.rs
Normal file
11
common/src/generation.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use vek::*;
|
||||
|
||||
pub struct NpcInfo {
|
||||
pub pos: Vec3<f32>,
|
||||
pub boss: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ChunkSupplement {
|
||||
pub npcs: Vec<NpcInfo>,
|
||||
}
|
@ -45,7 +45,7 @@ impl ChunkPath {
|
||||
pub fn chunk_get_neighbors<V: RectRasterableVol + ReadVol + Debug>(
|
||||
_vol: &VolGrid2d<V>,
|
||||
pos: &Vec2<i32>,
|
||||
) -> impl IntoIterator<Item = Vec2<i32>> {
|
||||
) -> impl Iterator<Item = Vec2<i32>> {
|
||||
let directions = vec![
|
||||
Vec2::new(1, 0), // Right chunk
|
||||
Vec2::new(-1, 0), // Left chunk
|
||||
@ -53,7 +53,14 @@ impl ChunkPath {
|
||||
Vec2::new(0, -1), // Bottom chunk
|
||||
];
|
||||
|
||||
let neighbors: Vec<Vec2<i32>> = directions.into_iter().map(|dir| dir + pos).collect();
|
||||
let mut neighbors = Vec::new();
|
||||
for x in -2..3 {
|
||||
for y in -2..3 {
|
||||
neighbors.push(pos + Vec2::new(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
//let neighbors: Vec<Vec2<i32>> = directions.into_iter().map(|dir| dir + pos).collect();
|
||||
|
||||
neighbors.into_iter()
|
||||
}
|
||||
@ -61,7 +68,7 @@ impl ChunkPath {
|
||||
&mut self,
|
||||
vol: &VolGrid2d<V>,
|
||||
pos: Vec3<i32>,
|
||||
) -> impl IntoIterator<Item = Vec3<i32>> {
|
||||
) -> impl Iterator<Item = Vec3<i32>> {
|
||||
let directions = vec![
|
||||
Vec3::new(0, 1, 0), // Forward
|
||||
Vec3::new(0, 1, 1), // Forward upward
|
||||
@ -102,7 +109,7 @@ impl ChunkPath {
|
||||
.any(|new_pos| new_pos.cmpeq(&vol.pos_key(pos)).iter().all(|e| *e));
|
||||
}
|
||||
_ => {
|
||||
println!("No chunk path");
|
||||
//println!("No chunk path");
|
||||
}
|
||||
}
|
||||
return is_walkable_position && is_within_chunk;
|
||||
@ -110,11 +117,11 @@ impl ChunkPath {
|
||||
pub fn get_worldpath<V: RectRasterableVol + ReadVol + Debug>(
|
||||
&mut self,
|
||||
vol: &VolGrid2d<V>,
|
||||
) -> WorldPath {
|
||||
) -> Result<WorldPath, ()> {
|
||||
let wp = WorldPath::new(vol, self.from, self.dest, |vol, pos| {
|
||||
self.worldpath_get_neighbors(vol, *pos)
|
||||
self.worldpath_get_neighbors(vol, pos)
|
||||
});
|
||||
println!("Fetching world path from hierarchical path: {:?}", wp);
|
||||
//println!("Fetching world path from hierarchical path: {:?}", wp);
|
||||
wp
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,11 @@ pub mod comp;
|
||||
pub mod effect;
|
||||
pub mod event;
|
||||
pub mod figure;
|
||||
pub mod generation;
|
||||
pub mod hierarchical;
|
||||
pub mod msg;
|
||||
pub mod npc;
|
||||
pub mod path;
|
||||
pub mod pathfinding;
|
||||
pub mod ray;
|
||||
pub mod region;
|
||||
|
127
common/src/path.rs
Normal file
127
common/src/path.rs
Normal file
@ -0,0 +1,127 @@
|
||||
use crate::{
|
||||
pathfinding::WorldPath,
|
||||
terrain::Block,
|
||||
vol::{BaseVol, ReadVol},
|
||||
};
|
||||
use std::iter::FromIterator;
|
||||
use vek::*;
|
||||
|
||||
// Path
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Path {
|
||||
nodes: Vec<Vec3<i32>>,
|
||||
}
|
||||
|
||||
impl FromIterator<Vec3<i32>> for Path {
|
||||
fn from_iter<I: IntoIterator<Item = Vec3<i32>>>(iter: I) -> Self {
|
||||
Self {
|
||||
nodes: iter.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn len(&self) -> usize {
|
||||
self.nodes.len()
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Option<Vec3<i32>> {
|
||||
self.nodes.first().copied()
|
||||
}
|
||||
|
||||
pub fn end(&self) -> Option<Vec3<i32>> {
|
||||
self.nodes.last().copied()
|
||||
}
|
||||
}
|
||||
|
||||
// Route: A path that can be progressed along
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Route {
|
||||
path: Path,
|
||||
next_idx: usize,
|
||||
}
|
||||
|
||||
impl From<Path> for Route {
|
||||
fn from(path: Path) -> Self {
|
||||
Self { path, next_idx: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl Route {
|
||||
pub fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn next(&self) -> Option<Vec3<i32>> {
|
||||
self.path.nodes.get(self.next_idx).copied()
|
||||
}
|
||||
|
||||
pub fn is_finished(&self) -> bool {
|
||||
self.next().is_none()
|
||||
}
|
||||
|
||||
pub fn traverse<V>(&mut self, vol: &V, pos: Vec3<f32>) -> Option<Vec3<f32>>
|
||||
where
|
||||
V: BaseVol<Vox = Block> + ReadVol,
|
||||
{
|
||||
let next = self.next()?;
|
||||
if vol.get(next).map(|b| b.is_solid()).unwrap_or(false) {
|
||||
None
|
||||
} else {
|
||||
let next_tgt = next.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0);
|
||||
if next_tgt.distance_squared(pos) < 1.0f32.powf(2.0) {
|
||||
self.next_idx += 1;
|
||||
}
|
||||
Some(next_tgt - pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chaser: A self-contained system that attempts to chase a moving target
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Chaser {
|
||||
route: Route,
|
||||
}
|
||||
|
||||
impl Chaser {
|
||||
pub fn chase<V>(&mut self, vol: &V, tgt: Vec3<f32>, pos: Vec3<f32>) -> Option<Vec3<f32>>
|
||||
where
|
||||
V: BaseVol<Vox = Block> + ReadVol,
|
||||
{
|
||||
let pos_to_tgt = pos.distance(tgt);
|
||||
|
||||
if pos_to_tgt < 4.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bearing = if let Some(end) = self.route.path().end() {
|
||||
let end_to_tgt = end.map(|e| e as f32).distance(tgt);
|
||||
if end_to_tgt > pos_to_tgt * 0.3 + 5.0 {
|
||||
None
|
||||
} else {
|
||||
self.route.traverse(vol, pos)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// TODO: What happens when we get stuck?
|
||||
if let Some(bearing) = bearing {
|
||||
Some(bearing)
|
||||
} else {
|
||||
let path: Path = WorldPath::find(vol, pos, tgt)
|
||||
.ok()
|
||||
.and_then(|wp| wp.path.map(|nodes| nodes.into_iter().rev()))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
self.route = path.into();
|
||||
|
||||
Some(tgt - pos)
|
||||
}
|
||||
}
|
||||
}
|
@ -13,30 +13,38 @@ pub struct WorldPath {
|
||||
}
|
||||
|
||||
impl WorldPath {
|
||||
pub fn new<V: ReadVol, I>(
|
||||
vol: &V,
|
||||
pub fn find<'a, V: ReadVol>(vol: &'a V, from: Vec3<f32>, dest: Vec3<f32>) -> Result<Self, ()> {
|
||||
Self::new(vol, from, dest, Self::get_neighbors)
|
||||
}
|
||||
|
||||
pub fn new<'a, V: ReadVol, I>(
|
||||
vol: &'a V,
|
||||
from: Vec3<f32>,
|
||||
dest: Vec3<f32>,
|
||||
get_neighbors: impl FnMut(&V, &Vec3<i32>) -> I,
|
||||
) -> Self
|
||||
get_neighbors: impl FnMut(&'a V, Vec3<i32>) -> I,
|
||||
) -> Result<Self, ()>
|
||||
where
|
||||
I: IntoIterator<Item = Vec3<i32>>,
|
||||
I: Iterator<Item = Vec3<i32>> + 'a,
|
||||
{
|
||||
let ifrom: Vec3<i32> = Vec3::from(from.map(|e| e.floor() as i32));
|
||||
let idest: Vec3<i32> = Vec3::from(dest.map(|e| e.floor() as i32));
|
||||
let path = WorldPath::get_path(vol, ifrom, idest, get_neighbors);
|
||||
let path = WorldPath::get_path(vol, ifrom, idest, get_neighbors).ok_or(())?;
|
||||
|
||||
Self { from, dest, path }
|
||||
Ok(Self {
|
||||
from,
|
||||
dest,
|
||||
path: Some(path),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_path<V: ReadVol, I>(
|
||||
vol: &V,
|
||||
pub fn get_path<'a, V: ReadVol, I>(
|
||||
vol: &'a V,
|
||||
from: Vec3<i32>,
|
||||
dest: Vec3<i32>,
|
||||
mut get_neighbors: impl FnMut(&V, &Vec3<i32>) -> I,
|
||||
mut get_neighbors: impl FnMut(&'a V, Vec3<i32>) -> I,
|
||||
) -> Option<Vec<Vec3<i32>>>
|
||||
where
|
||||
I: IntoIterator<Item = Vec3<i32>>,
|
||||
I: Iterator<Item = Vec3<i32>> + 'a,
|
||||
{
|
||||
let new_start = WorldPath::get_z_walkable_space(vol, from);
|
||||
let new_dest = WorldPath::get_z_walkable_space(vol, dest);
|
||||
@ -46,7 +54,7 @@ impl WorldPath {
|
||||
new_start,
|
||||
new_dest,
|
||||
euclidean_distance,
|
||||
|pos| get_neighbors(vol, pos),
|
||||
|pos| get_neighbors(vol, *pos),
|
||||
transition_cost,
|
||||
)
|
||||
} else {
|
||||
@ -55,46 +63,33 @@ impl WorldPath {
|
||||
}
|
||||
|
||||
fn get_z_walkable_space<V: ReadVol>(vol: &V, pos: Vec3<i32>) -> Option<Vec3<i32>> {
|
||||
if WorldPath::is_walkable_space(vol, pos) {
|
||||
return Some(pos);
|
||||
let mut z_incr = 0;
|
||||
for i in 0..32 {
|
||||
let test_pos = pos + Vec3::unit_z() * z_incr;
|
||||
if WorldPath::is_walkable_space(vol, test_pos) {
|
||||
return Some(test_pos);
|
||||
}
|
||||
z_incr = -z_incr + if z_incr <= 0 { 1 } else { 0 };
|
||||
}
|
||||
|
||||
let mut cur_pos_below = pos.clone();
|
||||
while !WorldPath::is_walkable_space(vol, cur_pos_below) && cur_pos_below.z > 0 {
|
||||
cur_pos_below.z -= 1;
|
||||
}
|
||||
|
||||
let max_z = 1000;
|
||||
let mut cur_pos_above = pos.clone();
|
||||
while !WorldPath::is_walkable_space(vol, cur_pos_above) && cur_pos_above.z <= max_z {
|
||||
cur_pos_above.z += 1;
|
||||
}
|
||||
|
||||
if cur_pos_below.z > 0 {
|
||||
Some(cur_pos_below)
|
||||
} else if cur_pos_above.z < max_z {
|
||||
Some(cur_pos_above)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_walkable_space<V: ReadVol>(vol: &V, pos: Vec3<i32>) -> bool {
|
||||
if let (Ok(voxel), Ok(upper_neighbor), Ok(upper_neighbor2)) = (
|
||||
vol.get(pos),
|
||||
vol.get(pos + Vec3::new(0, 0, 1)),
|
||||
vol.get(pos + Vec3::new(0, 0, 2)),
|
||||
) {
|
||||
!voxel.is_empty() && upper_neighbor.is_empty() && upper_neighbor2.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
vol.get(pos - Vec3::unit_z())
|
||||
.map(|v| !v.is_empty())
|
||||
.unwrap_or(false)
|
||||
&& (0..2).all(|z| {
|
||||
vol.get(pos + Vec3::new(0, 0, z))
|
||||
.map(|v| v.is_empty())
|
||||
.unwrap_or(true)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_neighbors<V: ReadVol>(
|
||||
vol: &V,
|
||||
pos: &Vec3<i32>,
|
||||
) -> impl IntoIterator<Item = Vec3<i32>> {
|
||||
pub fn get_neighbors<'a, V: ReadVol>(
|
||||
vol: &'a V,
|
||||
pos: Vec3<i32>,
|
||||
) -> impl Iterator<Item = Vec3<i32>> + 'a {
|
||||
let directions = vec![
|
||||
Vec3::new(0, 1, 0), // Forward
|
||||
Vec3::new(0, 1, 1), // Forward upward
|
||||
@ -115,13 +110,10 @@ impl WorldPath {
|
||||
Vec3::new(0, 0, -1), // Downwards
|
||||
];
|
||||
|
||||
let neighbors: Vec<Vec3<i32>> = directions
|
||||
directions
|
||||
.into_iter()
|
||||
.map(|dir| dir + pos)
|
||||
.filter(|new_pos| Self::is_walkable_space(vol, *new_pos))
|
||||
.collect();
|
||||
|
||||
neighbors.into_iter()
|
||||
.map(move |dir| dir + pos)
|
||||
.filter(move |new_pos| Self::is_walkable_space(vol, *new_pos))
|
||||
}
|
||||
|
||||
pub fn move_along_path<V: ReadVol>(
|
||||
@ -130,7 +122,7 @@ impl WorldPath {
|
||||
pos: &Pos,
|
||||
inputs: &mut ControllerInputs,
|
||||
is_destination: impl Fn(Vec3<i32>, Vec3<i32>) -> bool,
|
||||
found_destination: impl FnOnce(),
|
||||
mut found_destination: impl FnMut(),
|
||||
) {
|
||||
// No path available
|
||||
if self.path == None {
|
||||
@ -156,10 +148,8 @@ impl WorldPath {
|
||||
self.path = Some(block_path);
|
||||
}
|
||||
|
||||
let move_dir = Vec2::<i32>::from(next_pos - ipos);
|
||||
|
||||
// Move the input towards the next area on the path
|
||||
inputs.move_dir = Vec2::<f32>::new(move_dir.x as f32, move_dir.y as f32);
|
||||
inputs.move_dir = Vec2::from(next_pos.map(|e| (e as f32).floor() + 0.5) - pos.0);
|
||||
|
||||
// Need to jump to continue
|
||||
if next_pos.z >= ipos.z + 1 {
|
||||
@ -171,7 +161,11 @@ impl WorldPath {
|
||||
if next_pos.z - min_z_glide_height < ipos.z {
|
||||
inputs.glide.set_state(true);
|
||||
}
|
||||
} else {
|
||||
found_destination();
|
||||
}
|
||||
} else {
|
||||
found_destination();
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,9 +180,9 @@ impl WorldPath {
|
||||
}
|
||||
|
||||
pub fn euclidean_distance(start: &Vec3<i32>, end: &Vec3<i32>) -> f32 {
|
||||
start.map(|e| e as f32).distance(end.map(|e| e as f32))
|
||||
start.map(|e| e as f32).distance((*end).map(|e| e as f32))
|
||||
}
|
||||
|
||||
pub fn transition_cost(_start: &Vec3<i32>, _end: &Vec3<i32>) -> f32 {
|
||||
1.0f32
|
||||
1.0
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use crate::comp::{
|
||||
Agent, CharacterState, Controller, MountState, MovementState::Glide, Pos, Stats,
|
||||
};
|
||||
use crate::hierarchical::ChunkPath;
|
||||
use crate::pathfinding::WorldPath;
|
||||
use crate::terrain::TerrainGrid;
|
||||
use crate::{hierarchical::ChunkPath, path::Path, pathfinding::WorldPath, terrain::TerrainGrid};
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
use specs::{Entities, Join, ReadExpect, ReadStorage, System, WriteStorage};
|
||||
use vek::*;
|
||||
@ -75,7 +73,8 @@ impl<'a> System<'a> for Sys {
|
||||
* MAX_TRAVEL_DIST;
|
||||
new_path = Some(
|
||||
ChunkPath::new(&*terrain, pos.0, pos.0 + new_dest)
|
||||
.get_worldpath(&*terrain),
|
||||
.get_worldpath(&*terrain)
|
||||
.unwrap(),
|
||||
);
|
||||
};
|
||||
|
||||
@ -101,34 +100,39 @@ impl<'a> System<'a> for Sys {
|
||||
inputs.move_dir = bearing.normalized();
|
||||
}
|
||||
}
|
||||
Agent::Pet { target, offset } => {
|
||||
Agent::Pet { target, chaser } => {
|
||||
// Run towards target.
|
||||
match positions.get(*target) {
|
||||
Some(tgt_pos) => {
|
||||
let tgt_pos = tgt_pos.0 + *offset;
|
||||
|
||||
if tgt_pos.z > pos.0.z + 1.0 {
|
||||
inputs.jump.set_state(true);
|
||||
}
|
||||
|
||||
// Move towards the target.
|
||||
let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude();
|
||||
inputs.move_dir = if dist > 5.0 {
|
||||
Vec2::from(tgt_pos - pos.0).normalized()
|
||||
} else if dist < 1.5 && dist > 0.001 {
|
||||
Vec2::from(pos.0 - tgt_pos).normalized()
|
||||
} else {
|
||||
Vec2::zero()
|
||||
};
|
||||
if let Some(tgt_pos) = positions.get(*target) {
|
||||
if let Some(bearing) = chaser.chase(&*terrain, tgt_pos.0, pos.0) {
|
||||
inputs.move_dir = Vec2::from(bearing).normalized();
|
||||
inputs.jump.set_state(bearing.z > 0.9);
|
||||
}
|
||||
_ => inputs.move_dir = Vec2::zero(),
|
||||
}
|
||||
|
||||
// Change offset occasionally.
|
||||
if rand::random::<f32>() < 0.003 {
|
||||
*offset =
|
||||
Vec2::new(rand::random::<f32>() - 0.5, rand::random::<f32>() - 0.5)
|
||||
* 10.0;
|
||||
/*
|
||||
const HAPPY_DIST: i32 = 4;
|
||||
let plot_path = if let Some(dir) = route.traverse(&*terrain, pos.0) {
|
||||
inputs.move_dir = Vec2::from(dir).normalized();
|
||||
inputs.jump.set_state(dir.z > 0.9);
|
||||
|
||||
// Sometimes recalculate to avoid getting stuck
|
||||
rand::random::<f32>() < 0.005
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if plot_path {
|
||||
let path: Path = WorldPath::find(&*terrain, pos.0, tgt_pos.0)
|
||||
.ok()
|
||||
.and_then(|wp| wp.path.map(|nodes| nodes.into_iter().rev()))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
*route = path.into();
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
inputs.move_dir = Vec2::zero();
|
||||
}
|
||||
}
|
||||
Agent::Enemy { bearing, target } => {
|
||||
|
@ -4,6 +4,10 @@ version = "0.4.0"
|
||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
worldgen = ["server/worldgen"]
|
||||
default = ["worldgen"]
|
||||
|
||||
[dependencies]
|
||||
server = { package = "veloren-server", path = "../server" }
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
|
@ -4,6 +4,9 @@ version = "0.4.0"
|
||||
authors = ["Joshua Barretto <joshua.s.barretto@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
worldgen = []
|
||||
|
||||
[dependencies]
|
||||
common = { package = "veloren-common", path = "../common" }
|
||||
world = { package = "veloren-world", path = "../world" }
|
||||
@ -19,7 +22,7 @@ scan_fmt = "0.2.4"
|
||||
ron = "0.5.1"
|
||||
serde = "1.0.102"
|
||||
serde_derive = "1.0.102"
|
||||
rand = "0.7.2"
|
||||
rand = { version = "0.7.2", features = ["small_rng"] }
|
||||
chrono = "0.4.9"
|
||||
hashbrown = { version = "0.6.2", features = ["rayon", "serde", "nightly"] }
|
||||
crossbeam = "=0.7.2"
|
||||
|
@ -1,4 +1,6 @@
|
||||
use common::terrain::TerrainChunk;
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
use crate::test_world::World;
|
||||
use common::{generation::ChunkSupplement, terrain::TerrainChunk};
|
||||
use crossbeam::channel;
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use specs::Entity as EcsEntity;
|
||||
@ -7,7 +9,8 @@ use std::sync::{
|
||||
Arc,
|
||||
};
|
||||
use vek::*;
|
||||
use world::{ChunkSupplement, World};
|
||||
#[cfg(feature = "worldgen")]
|
||||
use world::World;
|
||||
|
||||
type ChunkGenResult = (
|
||||
Vec2<i32>,
|
||||
|
@ -537,7 +537,8 @@ fn handle_pathfind(server: &mut Server, player: EcsEntity, args: String, action:
|
||||
{
|
||||
let target = start_pos.0 + Vec3::new(x, y, z);
|
||||
let new_path = ChunkPath::new(&*server.state.terrain(), start_pos.0, target)
|
||||
.get_worldpath(&*server.state.terrain());
|
||||
.get_worldpath(&*server.state.terrain())
|
||||
.unwrap();
|
||||
|
||||
server.state.write_component(
|
||||
target_entity,
|
||||
@ -627,10 +628,7 @@ fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &
|
||||
fn alignment_to_agent(alignment: &str, target: EcsEntity) -> Option<comp::Agent> {
|
||||
match alignment {
|
||||
"hostile" => Some(comp::Agent::enemy()),
|
||||
"friendly" => Some(comp::Agent::Pet {
|
||||
target,
|
||||
offset: Vec2::zero(),
|
||||
}),
|
||||
"friendly" => Some(comp::Agent::pet(target)),
|
||||
"traveler" => Some(comp::Agent::Traveler {
|
||||
path: WorldPath::default(),
|
||||
}),
|
||||
@ -981,6 +979,20 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
fn handle_debug_column(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
_args: String,
|
||||
_action: &ChatCommand,
|
||||
) {
|
||||
server.notify_client(
|
||||
entity,
|
||||
ServerMsg::private(String::from("Unsupported without worldgen enabled")),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
fn handle_debug_column(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
let sim = server.world.sim();
|
||||
let sampler = server.world.sample_columns();
|
||||
|
@ -10,6 +10,8 @@ pub mod input;
|
||||
pub mod metrics;
|
||||
pub mod settings;
|
||||
pub mod sys;
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
mod test_world;
|
||||
|
||||
// Reexports
|
||||
pub use crate::{error::Error, input::Input, settings::ServerSettings};
|
||||
@ -46,10 +48,14 @@ use std::{
|
||||
};
|
||||
use uvth::{ThreadPool, ThreadPoolBuilder};
|
||||
use vek::*;
|
||||
#[cfg(feature = "worldgen")]
|
||||
use world::{
|
||||
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP, WORLD_SIZE},
|
||||
World,
|
||||
};
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
use test_world::World;
|
||||
|
||||
const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
|
||||
|
||||
pub enum Event {
|
||||
@ -108,6 +114,7 @@ impl Server {
|
||||
state.ecs_mut().register::<RegionSubscription>();
|
||||
state.ecs_mut().register::<Client>();
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
let world = World::generate(
|
||||
settings.world_seed,
|
||||
WorldOpts {
|
||||
@ -121,8 +128,14 @@ impl Server {
|
||||
..WorldOpts::default()
|
||||
},
|
||||
);
|
||||
#[cfg(feature = "worldgen")]
|
||||
let map = world.sim().get_map();
|
||||
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
let world = World::generate(settings.world_seed);
|
||||
let map = Vec::new();
|
||||
|
||||
#[cfg(feature = "worldgen")]
|
||||
let spawn_point = {
|
||||
// NOTE: all of these `.map(|e| e as [type])` calls should compile into no-ops,
|
||||
// but are needed to be explicit about casting (and to make the compiler stop complaining)
|
||||
@ -167,6 +180,9 @@ impl Server {
|
||||
Vec3::new(spawn_location.x, spawn_location.y, z).map(|e| (e as f32)) + 0.5
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "worldgen"))]
|
||||
let spawn_point = Vec3::new(0.0, 0.0, 256.0);
|
||||
|
||||
// set the spawn point we calculated above
|
||||
state.ecs_mut().insert(SpawnPoint(spawn_point));
|
||||
|
||||
|
41
server/src/test_world.rs
Normal file
41
server/src/test_world.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use common::{
|
||||
generation::{ChunkSupplement, NpcInfo},
|
||||
terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
||||
};
|
||||
use rand::{prelude::*, rngs::SmallRng};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
pub struct World;
|
||||
|
||||
impl World {
|
||||
pub fn generate(_seed: u32) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn tick(&self, dt: Duration) {}
|
||||
|
||||
pub fn generate_chunk(
|
||||
&self,
|
||||
chunk_pos: Vec2<i32>,
|
||||
_should_continue: impl FnMut() -> bool,
|
||||
) -> Result<(TerrainChunk, ChunkSupplement), ()> {
|
||||
let (x, y) = chunk_pos.map(|e| e.to_le_bytes()).into_tuple();
|
||||
let mut rng = SmallRng::from_seed([
|
||||
x[0], x[1], x[2], x[3], y[0], y[1], y[2], y[3], x[0], x[1], x[2], x[3], y[0], y[1],
|
||||
y[2], y[3],
|
||||
]);
|
||||
let height = rng.gen::<i32>() % 8;
|
||||
|
||||
Ok((
|
||||
TerrainChunk::new(
|
||||
256 + if rng.gen::<u8>() < 64 { height } else { 0 },
|
||||
Block::new(BlockKind::Dense, Rgb::new(200, 220, 255)),
|
||||
Block::empty(),
|
||||
TerrainChunkMeta::void(),
|
||||
),
|
||||
ChunkSupplement::default(),
|
||||
))
|
||||
}
|
||||
}
|
@ -1259,8 +1259,8 @@ impl<V: RectRasterableVol> Terrain<V> {
|
||||
.fold(i32::MIN, |max, (_, chunk)| chunk.get_max_z().max(max));
|
||||
|
||||
let aabb = Aabb {
|
||||
min: Vec3::from(aabr.min) + Vec3::unit_z() * (min_z - 1),
|
||||
max: Vec3::from(aabr.max) + Vec3::unit_z() * (max_z + 1),
|
||||
min: Vec3::from(aabr.min) + Vec3::unit_z() * (min_z - 2),
|
||||
max: Vec3::from(aabr.max) + Vec3::unit_z() * (max_z + 2),
|
||||
};
|
||||
|
||||
// Clone various things so that they can be moved into the thread.
|
||||
|
@ -19,6 +19,7 @@ use crate::{
|
||||
util::Sampler,
|
||||
};
|
||||
use common::{
|
||||
generation::{ChunkSupplement, NpcInfo},
|
||||
terrain::{Block, BlockKind, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||
vol::{ReadVol, RectVolSize, Vox, WriteVol},
|
||||
};
|
||||
@ -165,18 +166,3 @@ impl World {
|
||||
Ok((chunk, supplement))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NpcInfo {
|
||||
pub pos: Vec3<f32>,
|
||||
pub boss: bool,
|
||||
}
|
||||
|
||||
pub struct ChunkSupplement {
|
||||
pub npcs: Vec<NpcInfo>,
|
||||
}
|
||||
|
||||
impl Default for ChunkSupplement {
|
||||
fn default() -> Self {
|
||||
Self { npcs: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user