Pathfinding improvements

This commit is contained in:
Joshua Barretto 2020-07-30 20:26:49 +01:00
parent f9baaf7de1
commit a489915845
3 changed files with 61 additions and 47 deletions

View File

@ -64,7 +64,7 @@ impl<'a> From<&'a Body> for Psyche {
fn from(body: &'a Body) -> Self { fn from(body: &'a Body) -> Self {
Self { Self {
aggro: match body { aggro: match body {
Body::Humanoid(_) => 0.8, Body::Humanoid(_) => 0.5,
Body::QuadrupedSmall(_) => 0.35, Body::QuadrupedSmall(_) => 0.35,
Body::QuadrupedMedium(_) => 0.5, Body::QuadrupedMedium(_) => 0.5,
Body::QuadrupedLow(_) => 0.65, Body::QuadrupedLow(_) => 0.65,

View File

@ -4,6 +4,7 @@ use crate::{
vol::{BaseVol, ReadVol}, vol::{BaseVol, ReadVol},
}; };
use hashbrown::hash_map::DefaultHashBuilder; use hashbrown::hash_map::DefaultHashBuilder;
use rand::prelude::*;
use std::iter::FromIterator; use std::iter::FromIterator;
use vek::*; use vek::*;
@ -67,6 +68,17 @@ pub struct TraversalConfig {
pub min_tgt_dist: f32, pub min_tgt_dist: f32,
} }
const DIAGONALS: [Vec2<i32>; 8] = [
Vec2::new(1, 0),
Vec2::new(1, 1),
Vec2::new(0, 1),
Vec2::new(-1, 1),
Vec2::new(-1, 0),
Vec2::new(-1, -1),
Vec2::new(0, -1),
Vec2::new(1, -1),
];
impl Route { impl Route {
pub fn path(&self) -> &Path<Vec3<i32>> { &self.path } pub fn path(&self) -> &Path<Vec3<i32>> { &self.path }
@ -90,42 +102,31 @@ impl Route {
let next0 = self let next0 = self
.next(0) .next(0)
.unwrap_or_else(|| pos.map(|e| e.floor() as i32)); .unwrap_or_else(|| pos.map(|e| e.floor() as i32));
let next1 = self.next(1).unwrap_or(next0);
// Stop using obstructed paths // Stop using obstructed paths
if vol.get(next0).map(|b| b.is_solid()).unwrap_or(false) { if !walkable(vol, next1) {
return None; return None;
} }
let diagonals = [ let be_precise = DIAGONALS.iter().any(|pos| {
Vec2::new(1, 0), (-2..2)
Vec2::new(1, 1), .all(|z| vol.get(next0 + Vec3::new(pos.x, pos.y, z))
Vec2::new(0, 1), .map(|b| !b.is_solid())
Vec2::new(-1, 1), .unwrap_or(false))
Vec2::new(-1, 0),
Vec2::new(-1, -1),
Vec2::new(0, -1),
Vec2::new(1, -1),
];
let next1 = self.next(1).unwrap_or(next0);
let be_precise = diagonals.iter().any(|pos| {
!walkable(vol, next0 + Vec3::new(pos.x, pos.y, 0))
&& !walkable(vol, next0 + Vec3::new(pos.x, pos.y, -1))
&& !walkable(vol, next0 + Vec3::new(pos.x, pos.y, -2))
&& !walkable(vol, next0 + Vec3::new(pos.x, pos.y, 1))
}); });
let next0_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); let next0_tgt = next0.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0);
let next1_tgt = next1.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0); let next1_tgt = next1.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0);
let next_tgt = next0_tgt; let next_tgt = next0_tgt;
// Maybe skip a node (useful with traversing downhill) // // Maybe skip a node (useful with traversing downhill)
let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) { // let closest_tgt = if next0_tgt.distance_squared(pos) < next1_tgt.distance_squared(pos) {
next0_tgt // next0_tgt
} else { // } else {
next1_tgt // next1_tgt
}; // };
let closest_tgt = next0_tgt.map2(pos, |tgt, pos| pos.clamped(tgt.floor(), tgt.ceil()));
// Determine whether we're close enough to the next to to consider it completed // Determine whether we're close enough to the next to to consider it completed
let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy()); let dist_sqrd = pos.xy().distance_squared(closest_tgt.xy());
@ -349,9 +350,7 @@ impl Chaser {
// theory this shouldn't happen, but in practice the world is full // theory this shouldn't happen, but in practice the world is full
// of unpredictable obstacles that are more than willing to mess up // of unpredictable obstacles that are more than willing to mess up
// our day. TODO: Come up with a better heuristic for this // our day. TODO: Come up with a better heuristic for this
if end_to_tgt > pos_to_tgt * 0.3 + 5.0 if end_to_tgt > pos_to_tgt * 0.3 + 5.0 || thread_rng().gen::<f32>() < 0.001 {
/* || thread_rng().gen::<f32>() < 0.005 */
{
None None
} else { } else {
self.route self.route
@ -393,7 +392,12 @@ impl Chaser {
}); });
} }
if walkable(vol, (pos + Vec3::<f32>::from(tgt_dir) * 3.0).map(|e| e as i32)) { let walking_towards_edge = (-3..2)
.all(|z| vol.get((pos + Vec3::<f32>::from(tgt_dir) * 2.5).map(|e| e as i32) + Vec3::unit_z() * z)
.map(|b| !b.is_solid())
.unwrap_or(false));
if !walking_towards_edge {
Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75)) Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75))
} else { } else {
None None
@ -454,23 +458,27 @@ where
let heuristic = |pos: &Vec3<i32>| (pos.distance_squared(end) as f32).sqrt(); let heuristic = |pos: &Vec3<i32>| (pos.distance_squared(end) as f32).sqrt();
let neighbors = |pos: &Vec3<i32>| { let neighbors = |pos: &Vec3<i32>| {
let pos = *pos; let pos = *pos;
const DIRS: [Vec3<i32>; 17] = [ const DIRS: [Vec3<i32>; 21] = [
Vec3::new(0, 1, 0), // Forward Vec3::new(0, 1, 0), // Forward
Vec3::new(0, 1, 1), // Forward upward Vec3::new(0, 1, 1), // Forward upward
Vec3::new(0, 1, 2), // Forward Upwardx2 Vec3::new(0, 1, 2), // Forward Upwardx2
Vec3::new(0, 1, -1), // Forward downward Vec3::new(0, 1, -1), // Forward downward
Vec3::new(0, 1, -2), // Forward downwardx2
Vec3::new(1, 0, 0), // Right Vec3::new(1, 0, 0), // Right
Vec3::new(1, 0, 1), // Right upward Vec3::new(1, 0, 1), // Right upward
Vec3::new(1, 0, 2), // Right Upwardx2 Vec3::new(1, 0, 2), // Right Upwardx2
Vec3::new(1, 0, -1), // Right downward Vec3::new(1, 0, -1), // Right downward
Vec3::new(1, 0, -2), // Right downwardx2
Vec3::new(0, -1, 0), // Backwards Vec3::new(0, -1, 0), // Backwards
Vec3::new(0, -1, 1), // Backward Upward Vec3::new(0, -1, 1), // Backward Upward
Vec3::new(0, -1, 2), // Backward Upwardx2 Vec3::new(0, -1, 2), // Backward Upwardx2
Vec3::new(0, -1, -1), // Backward downward Vec3::new(0, -1, -1), // Backward downward
Vec3::new(0, -1, -2), // Backward downwardx2
Vec3::new(-1, 0, 0), // Left Vec3::new(-1, 0, 0), // Left
Vec3::new(-1, 0, 1), // Left upward Vec3::new(-1, 0, 1), // Left upward
Vec3::new(-1, 0, 2), // Left Upwardx2 Vec3::new(-1, 0, 2), // Left Upwardx2
Vec3::new(-1, 0, -1), // Left downward Vec3::new(-1, 0, -1), // Left downward
Vec3::new(-1, 0, -2), // Left downwardx2
Vec3::new(0, 0, -1), // Downwards Vec3::new(0, 0, -1), // Downwards
]; ];

View File

@ -159,7 +159,7 @@ impl<'a> System<'a> for Sys {
// and so can afford to be less precise when trying to move around // and so can afford to be less precise when trying to move around
// the world (especially since they would otherwise get stuck on // the world (especially since they would otherwise get stuck on
// obstacles that smaller entities would not). // obstacles that smaller entities would not).
let node_tolerance = scale + vel.0.xy().magnitude() * 0.2; let node_tolerance = scale * 1.5;
let slow_factor = body.map(|b| b.base_accel() / 250.0).unwrap_or(0.0).min(1.0); let slow_factor = body.map(|b| b.base_accel() / 250.0).unwrap_or(0.0).min(1.0);
let mut do_idle = false; let mut do_idle = false;
@ -306,7 +306,10 @@ impl<'a> System<'a> for Sys {
.unwrap_or(0.5); .unwrap_or(0.5);
// Flee // Flee
if 1.0 - agent.psyche.aggro > damage { let flees = alignment
.map(|a| !matches!(a, Alignment::Enemy | Alignment::Owned(_)))
.unwrap_or(true);
if 1.0 - agent.psyche.aggro > damage && flees {
if dist_sqrd < MAX_FLEE_DIST.powf(2.0) { if dist_sqrd < MAX_FLEE_DIST.powf(2.0) {
if let Some((bearing, speed)) = chaser.chase( if let Some((bearing, speed)) = chaser.chase(
&*terrain, &*terrain,
@ -464,7 +467,7 @@ impl<'a> System<'a> for Sys {
// Attack a target that's attacking us // Attack a target that's attacking us
if let Some(my_stats) = stats.get(entity) { if let Some(my_stats) = stats.get(entity) {
// Only if the attack was recent // Only if the attack was recent
if my_stats.health.last_change.0 < 5.0 { if my_stats.health.last_change.0 < 3.0 {
if let comp::HealthSource::Attack { by } if let comp::HealthSource::Attack { by }
| comp::HealthSource::Projectile { owner: Some(by) } = | comp::HealthSource::Projectile { owner: Some(by) } =
my_stats.health.last_change.1.cause my_stats.health.last_change.1.cause
@ -473,20 +476,23 @@ impl<'a> System<'a> for Sys {
if let Some(attacker) = uid_allocator.retrieve_entity_internal(by.id()) if let Some(attacker) = uid_allocator.retrieve_entity_internal(by.id())
{ {
if stats.get(attacker).map_or(false, |a| !a.is_dead) { if stats.get(attacker).map_or(false, |a| !a.is_dead) {
if agent.can_speak { match agent.activity {
let msg = "npc.speech.villager_under_attack".to_string(); Activity::Attack { target, .. } if target == attacker => {},
event_bus.emit_now(ServerEvent::Chat( _ => {
UnresolvedChatMsg::npc(*uid, msg), if agent.can_speak {
)); let msg = "npc.speech.villager_under_attack".to_string();
} event_bus.emit_now(ServerEvent::Chat(UnresolvedChatMsg::npc(*uid, msg)));
}
agent.activity = Activity::Attack { agent.activity = Activity::Attack {
target: attacker, target: attacker,
chaser: Chaser::default(), chaser: Chaser::default(),
time: time.0, time: time.0,
been_close: false, been_close: false,
powerup: 0.0, powerup: 0.0,
}; };
},
}
} }
} }
} }