mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added sequence combinator, NPC site-site pathfinding
This commit is contained in:
parent
b2f92e4a6c
commit
9413a56c13
@ -109,6 +109,28 @@ pub trait Action<R = ()>: Any + Send + Sync {
|
||||
{
|
||||
Map(self, f, PhantomData)
|
||||
}
|
||||
fn boxed(self) -> Box<dyn Action<R>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: 'static> Action<R> for Box<dyn Action<R>> {
|
||||
fn is_same(&self, other: &Self) -> bool { (**self).dyn_is_same(other) }
|
||||
|
||||
fn dyn_is_same(&self, other: &dyn Action<R>) -> bool {
|
||||
match (other as &dyn Any).downcast_ref::<Self>() {
|
||||
Some(other) => self.is_same(other),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) { (**self).reset(); }
|
||||
|
||||
// TODO: Reset closure state?
|
||||
fn tick(&mut self, ctx: &mut NpcCtx) -> ControlFlow<R> { (**self).tick(ctx) }
|
||||
}
|
||||
|
||||
// Now
|
||||
@ -164,6 +186,23 @@ where
|
||||
Just(f, PhantomData)
|
||||
}
|
||||
|
||||
// Finish
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Finish;
|
||||
|
||||
impl Action<()> for Finish {
|
||||
fn is_same(&self, other: &Self) -> bool { true }
|
||||
|
||||
fn dyn_is_same(&self, other: &dyn Action<()>) -> bool { self.dyn_is_same_sized(other) }
|
||||
|
||||
fn reset(&mut self) {}
|
||||
|
||||
fn tick(&mut self, ctx: &mut NpcCtx) -> ControlFlow<()> { ControlFlow::Break(()) }
|
||||
}
|
||||
|
||||
pub fn finish() -> Finish { Finish }
|
||||
|
||||
// Tree
|
||||
|
||||
pub type Priority = usize;
|
||||
@ -293,6 +332,49 @@ impl<R: Send + Sync + 'static, A: Action<R>> Action<!> for Repeat<A, R> {
|
||||
}
|
||||
}
|
||||
|
||||
// Sequence
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Sequence<I, A, R = ()>(I, I, Option<A>, PhantomData<R>);
|
||||
|
||||
impl<R: Send + Sync + 'static, I: Iterator<Item = A> + Clone + Send + Sync + 'static, A: Action<R>>
|
||||
Action<()> for Sequence<I, A, R>
|
||||
{
|
||||
fn is_same(&self, other: &Self) -> bool { true }
|
||||
|
||||
fn dyn_is_same(&self, other: &dyn Action<()>) -> bool { self.dyn_is_same_sized(other) }
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.0 = self.1.clone();
|
||||
self.2 = None;
|
||||
}
|
||||
|
||||
fn tick(&mut self, ctx: &mut NpcCtx) -> ControlFlow<()> {
|
||||
let item = if let Some(prev) = &mut self.2 {
|
||||
prev
|
||||
} else {
|
||||
match self.0.next() {
|
||||
Some(next) => self.2.insert(next),
|
||||
None => return ControlFlow::Break(()),
|
||||
}
|
||||
};
|
||||
|
||||
if let ControlFlow::Break(_) = item.tick(ctx) {
|
||||
self.2 = None;
|
||||
}
|
||||
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seq<I, A, R>(iter: I) -> Sequence<I, A, R>
|
||||
where
|
||||
I: Iterator<Item = A> + Clone,
|
||||
A: Action<R>,
|
||||
{
|
||||
Sequence(iter.clone(), iter, None, PhantomData)
|
||||
}
|
||||
|
||||
// StopIf
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -6,7 +6,8 @@
|
||||
generators,
|
||||
trait_alias,
|
||||
trait_upcasting,
|
||||
control_flow_enum
|
||||
control_flow_enum,
|
||||
let_chains
|
||||
)]
|
||||
|
||||
pub mod data;
|
||||
|
@ -3,8 +3,8 @@ use std::{collections::VecDeque, hash::BuildHasherDefault};
|
||||
use crate::{
|
||||
data::{
|
||||
npc::{
|
||||
casual, choose, just, now, urgent, Action, Brain, Controller, Npc, NpcId, PathData,
|
||||
PathingMemory,
|
||||
casual, choose, finish, just, now, seq, urgent, watch, Action, Brain, Controller, Npc,
|
||||
NpcId, PathData, PathingMemory,
|
||||
},
|
||||
Sites,
|
||||
},
|
||||
@ -371,10 +371,16 @@ pub struct NpcCtx<'a> {
|
||||
controller: &'a mut Controller,
|
||||
}
|
||||
|
||||
fn idle() -> impl Action + Clone { just(|ctx| *ctx.controller = Controller::idle()) }
|
||||
fn pass() -> impl Action { just(|ctx| {}) }
|
||||
|
||||
fn move_toward(wpos: Vec3<f32>, speed_factor: f32) -> impl Action + Clone {
|
||||
const STEP_DIST: f32 = 10.0;
|
||||
fn idle_wait() -> impl Action {
|
||||
just(|ctx| *ctx.controller = Controller::idle())
|
||||
.repeat()
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
fn move_toward(wpos: Vec3<f32>, speed_factor: f32) -> impl Action {
|
||||
const STEP_DIST: f32 = 16.0;
|
||||
just(move |ctx| {
|
||||
let rpos = wpos - ctx.npc.wpos;
|
||||
let len = rpos.magnitude();
|
||||
@ -385,8 +391,8 @@ fn move_toward(wpos: Vec3<f32>, speed_factor: f32) -> impl Action + Clone {
|
||||
})
|
||||
}
|
||||
|
||||
fn goto(wpos: Vec3<f32>, speed_factor: f32) -> impl Action + Clone {
|
||||
const MIN_DIST: f32 = 1.0;
|
||||
fn goto(wpos: Vec3<f32>, speed_factor: f32) -> impl Action {
|
||||
const MIN_DIST: f32 = 2.0;
|
||||
|
||||
move_toward(wpos, speed_factor)
|
||||
.repeat()
|
||||
@ -394,6 +400,61 @@ fn goto(wpos: Vec3<f32>, speed_factor: f32) -> impl Action + Clone {
|
||||
.map(|_| {})
|
||||
}
|
||||
|
||||
fn goto_2d(wpos2d: Vec2<f32>, speed_factor: f32) -> impl Action {
|
||||
const MIN_DIST: f32 = 2.0;
|
||||
|
||||
now(move |ctx| {
|
||||
let wpos = wpos2d.with_z(
|
||||
ctx.ctx
|
||||
.world
|
||||
.sim()
|
||||
.get_alt_approx(wpos2d.as_())
|
||||
.unwrap_or(0.0),
|
||||
);
|
||||
goto(wpos, speed_factor)
|
||||
})
|
||||
}
|
||||
|
||||
fn path_to_site(tgt_site: SiteId) -> impl Action {
|
||||
now(move |ctx| {
|
||||
let sites = &ctx.ctx.state.data().sites;
|
||||
|
||||
// If we can, try to find a path to the site via tracks
|
||||
if let Some(current_site) = ctx.npc.current_site
|
||||
&& let Some((mut tracks, _)) = path_towns(current_site, tgt_site, sites, ctx.ctx.world)
|
||||
{
|
||||
seq(tracks
|
||||
.path
|
||||
.into_iter()
|
||||
.map(|(track_id, reversed)| now(move |ctx| {
|
||||
let track_len = ctx.ctx.world.civs().tracks.get(track_id).path().len();
|
||||
seq(if reversed {
|
||||
itertools::Either::Left((0..track_len).rev())
|
||||
} else {
|
||||
itertools::Either::Right(0..track_len)
|
||||
}
|
||||
.map(move |node_idx| now(move |ctx| {
|
||||
let track = ctx.ctx.world.civs().tracks.get(track_id);
|
||||
let next_node = track.path().nodes[node_idx];
|
||||
|
||||
let chunk_wpos = TerrainChunkSize::center_wpos(next_node);
|
||||
let path_wpos2d = ctx.ctx.world.sim()
|
||||
.get_nearest_path(chunk_wpos)
|
||||
.map_or(chunk_wpos, |(_, wpos, _, _)| wpos.as_());
|
||||
|
||||
goto_2d(path_wpos2d.as_(), 1.0)
|
||||
})))
|
||||
})))
|
||||
.boxed()
|
||||
} else if let Some(site) = sites.get(tgt_site) {
|
||||
// If all else fails, just walk toward the site in a straight line
|
||||
goto_2d(site.wpos.map(|e| e as f32 + 0.5), 1.0).boxed()
|
||||
} else {
|
||||
pass().boxed()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Seconds
|
||||
fn timeout(ctx: &NpcCtx, time: f64) -> impl FnMut(&mut NpcCtx) -> bool + Clone + Send + Sync {
|
||||
let end = ctx.ctx.event.time.0 + time;
|
||||
@ -404,7 +465,7 @@ fn think() -> impl Action {
|
||||
choose(|ctx| {
|
||||
if matches!(ctx.npc.profession, Some(Profession::Adventurer(_))) {
|
||||
// Choose a random site that's fairly close by
|
||||
let site_wpos2d = ctx
|
||||
if let Some(tgt_site) = ctx
|
||||
.ctx
|
||||
.state
|
||||
.data()
|
||||
@ -417,39 +478,20 @@ fn think() -> impl Action {
|
||||
})
|
||||
.min_by_key(|(_, site)| site.wpos.as_().distance(ctx.npc.wpos.xy()) as i32)
|
||||
.map(|(site_id, _)| site_id)
|
||||
.and_then(|tgt_site| {
|
||||
ctx.ctx
|
||||
.state
|
||||
.data()
|
||||
.sites
|
||||
.get(tgt_site)
|
||||
.map(|site| site.wpos)
|
||||
});
|
||||
|
||||
if let Some(site_wpos2d) = site_wpos2d {
|
||||
// Walk toward the site
|
||||
casual(goto(
|
||||
site_wpos2d.map(|e| e as f32 + 0.5).with_z(
|
||||
ctx.ctx
|
||||
.world
|
||||
.sim()
|
||||
.get_alt_approx(site_wpos2d.as_())
|
||||
.unwrap_or(0.0),
|
||||
),
|
||||
1.0,
|
||||
))
|
||||
{
|
||||
casual(path_to_site(tgt_site))
|
||||
} else {
|
||||
casual(idle())
|
||||
casual(pass())
|
||||
}
|
||||
} else if matches!(ctx.npc.profession, Some(Profession::Blacksmith)) {
|
||||
casual(idle())
|
||||
casual(idle_wait())
|
||||
} else {
|
||||
casual(
|
||||
now(|ctx| goto(ctx.npc.wpos + Vec3::unit_x() * 10.0, 1.0))
|
||||
.then(now(|ctx| goto(ctx.npc.wpos - Vec3::unit_x() * 10.0, 1.0)))
|
||||
.repeat()
|
||||
.stop_if(timeout(ctx, 10.0))
|
||||
.then(now(|ctx| idle().repeat().stop_if(timeout(ctx, 5.0))))
|
||||
.then(now(|ctx| idle_wait().stop_if(timeout(ctx, 5.0))))
|
||||
.map(|_| {}),
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user