mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added interrupt_with combinator, guard patrol patterns
This commit is contained in:
parent
a7a08763f2
commit
ce5ef481e1
@ -134,6 +134,36 @@ pub trait Action<R = ()>: Any + Send + Sync {
|
|||||||
StopIf(self, f.clone(), f)
|
StopIf(self, f.clone(), f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pause an action to possibly perform another action.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// // Keep going on adventures until your 111th birthday
|
||||||
|
/// walk_to_the_shops()
|
||||||
|
/// .interrupt_with(|ctx| if ctx.npc.is_hungry() {
|
||||||
|
/// Some(eat_food())
|
||||||
|
/// } else {
|
||||||
|
/// None
|
||||||
|
/// })
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
fn interrupt_with<A1: Action<R1>, R1, F: FnMut(&mut NpcCtx) -> Option<A1> + Clone>(
|
||||||
|
self,
|
||||||
|
f: F,
|
||||||
|
) -> InterruptWith<Self, F, A1, R1>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
InterruptWith {
|
||||||
|
a0: self,
|
||||||
|
f: f.clone(),
|
||||||
|
f2: f,
|
||||||
|
a1: None,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Map the completion value of this action to something else.
|
/// Map the completion value of this action to something else.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn map<F: FnMut(R) -> R1, R1>(self, f: F) -> Map<Self, F, R>
|
fn map<F: FnMut(R) -> R1, R1>(self, f: F) -> Map<Self, F, R>
|
||||||
@ -606,6 +636,62 @@ impl<A0: Action<R0>, A1: Action<R1>, R0: Send + Sync + 'static, R1: Send + Sync
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InterruptWith
|
||||||
|
|
||||||
|
/// See [`Action::then`].
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct InterruptWith<A0, F, A1, R1> {
|
||||||
|
a0: A0,
|
||||||
|
f: F,
|
||||||
|
f2: F,
|
||||||
|
a1: Option<A1>,
|
||||||
|
phantom: PhantomData<R1>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
A0: Action<R0>,
|
||||||
|
A1: Action<R1>,
|
||||||
|
F: FnMut(&mut NpcCtx) -> Option<A1> + Clone + Send + Sync + 'static,
|
||||||
|
R0: Send + Sync + 'static,
|
||||||
|
R1: Send + Sync + 'static,
|
||||||
|
> Action<R0> for InterruptWith<A0, F, A1, R1>
|
||||||
|
{
|
||||||
|
fn is_same(&self, other: &Self) -> bool { self.a0.is_same(&other.a0) }
|
||||||
|
|
||||||
|
fn dyn_is_same(&self, other: &dyn Action<R0>) -> bool { self.dyn_is_same_sized(other) }
|
||||||
|
|
||||||
|
fn backtrace(&self, bt: &mut Vec<String>) {
|
||||||
|
if let Some(a1) = &self.a1 {
|
||||||
|
// TODO: Find a way to represent interrupts in backtraces
|
||||||
|
bt.push("<interrupted>".to_string());
|
||||||
|
a1.backtrace(bt);
|
||||||
|
} else {
|
||||||
|
self.a0.backtrace(bt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.a0.reset();
|
||||||
|
self.f = self.f2.clone();
|
||||||
|
self.a1 = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick(&mut self, ctx: &mut NpcCtx) -> ControlFlow<R0> {
|
||||||
|
if let Some(new_a1) = (self.f)(ctx) {
|
||||||
|
self.a1 = Some(new_a1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(a1) = &mut self.a1 {
|
||||||
|
match a1.tick(ctx) {
|
||||||
|
ControlFlow::Continue(()) => return ControlFlow::Continue(()),
|
||||||
|
ControlFlow::Break(_) => self.a1 = None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.a0.tick(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Repeat
|
// Repeat
|
||||||
|
|
||||||
/// See [`Action::repeat`].
|
/// See [`Action::repeat`].
|
||||||
|
@ -560,6 +560,18 @@ fn find_forest(ctx: &mut NpcCtx) -> Option<Vec2<f32>> {
|
|||||||
.map(|chunk| TerrainChunkSize::center_wpos(chunk).as_())
|
.map(|chunk| TerrainChunkSize::center_wpos(chunk).as_())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn choose_plaza(ctx: &mut NpcCtx, site: SiteId) -> Option<Vec2<f32>> {
|
||||||
|
ctx.state
|
||||||
|
.data()
|
||||||
|
.sites
|
||||||
|
.get(site)
|
||||||
|
.and_then(|site| ctx.index.sites.get(site.world_site?).site2())
|
||||||
|
.and_then(|site2| {
|
||||||
|
let plaza = &site2.plots[site2.plazas().choose(&mut ctx.rng)?];
|
||||||
|
Some(site2.tile_center_wpos(plaza.root_tile()).as_())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn villager(visiting_site: SiteId) -> impl Action {
|
fn villager(visiting_site: SiteId) -> impl Action {
|
||||||
choose(move |ctx| {
|
choose(move |ctx| {
|
||||||
/*
|
/*
|
||||||
@ -645,6 +657,29 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
.map(|_| ()),
|
.map(|_| ()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if matches!(ctx.npc.profession, Some(Profession::Guard)) && ctx.rng.gen_bool(0.5) {
|
||||||
|
if let Some(plaza_wpos) = choose_plaza(ctx, visiting_site) {
|
||||||
|
return important(
|
||||||
|
travel_to_point(plaza_wpos, 0.45)
|
||||||
|
.debug(|| "patrol")
|
||||||
|
.interrupt_with(|ctx| {
|
||||||
|
if ctx.rng.gen_bool(0.0003) {
|
||||||
|
let phrase = *[
|
||||||
|
"My brother's out fighting ogres. What do I get? Guard duty...",
|
||||||
|
"Just one more patrol, then I can head home",
|
||||||
|
"No bandits are going to get past me",
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.choose(&mut ctx.rng)
|
||||||
|
.unwrap(); // Can't fail
|
||||||
|
Some(just(move |ctx| ctx.controller.say(None, phrase)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|_| ()),
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if matches!(ctx.npc.profession, Some(Profession::Merchant)) && ctx.rng.gen_bool(0.8)
|
} else if matches!(ctx.npc.profession, Some(Profession::Merchant)) && ctx.rng.gen_bool(0.8)
|
||||||
{
|
{
|
||||||
return casual(
|
return casual(
|
||||||
@ -688,17 +723,7 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
// If nothing else needs doing, walk between plazas and socialize
|
// If nothing else needs doing, walk between plazas and socialize
|
||||||
casual(now(move |ctx| {
|
casual(now(move |ctx| {
|
||||||
// Choose a plaza in the site we're visiting to walk to
|
// Choose a plaza in the site we're visiting to walk to
|
||||||
if let Some(plaza_wpos) = ctx
|
if let Some(plaza_wpos) = choose_plaza(ctx, visiting_site) {
|
||||||
.state
|
|
||||||
.data()
|
|
||||||
.sites
|
|
||||||
.get(visiting_site)
|
|
||||||
.and_then(|site| ctx.index.sites.get(site.world_site?).site2())
|
|
||||||
.and_then(|site2| {
|
|
||||||
let plaza = &site2.plots[site2.plazas().choose(&mut ctx.rng)?];
|
|
||||||
Some(site2.tile_center_wpos(plaza.root_tile()).as_())
|
|
||||||
})
|
|
||||||
{
|
|
||||||
// Walk to the plaza...
|
// Walk to the plaza...
|
||||||
Either::Left(travel_to_point(plaza_wpos, 0.5)
|
Either::Left(travel_to_point(plaza_wpos, 0.5)
|
||||||
.debug(|| "walk to plaza"))
|
.debug(|| "walk to plaza"))
|
||||||
|
@ -464,6 +464,8 @@ fn set_owner_if_no_target(bdata: &mut BehaviorData) -> bool {
|
|||||||
false,
|
false,
|
||||||
owner_pos,
|
owner_pos,
|
||||||
));
|
));
|
||||||
|
// Always become aware of our owner no matter what
|
||||||
|
bdata.agent.awareness.set_maximally_aware();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user