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)
|
||||
}
|
||||
|
||||
/// 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.
|
||||
#[must_use]
|
||||
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
|
||||
|
||||
/// See [`Action::repeat`].
|
||||
|
@ -560,6 +560,18 @@ fn find_forest(ctx: &mut NpcCtx) -> Option<Vec2<f32>> {
|
||||
.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 {
|
||||
choose(move |ctx| {
|
||||
/*
|
||||
@ -645,6 +657,29 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
||||
.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)
|
||||
{
|
||||
return casual(
|
||||
@ -688,17 +723,7 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
||||
// If nothing else needs doing, walk between plazas and socialize
|
||||
casual(now(move |ctx| {
|
||||
// Choose a plaza in the site we're visiting to walk to
|
||||
if let Some(plaza_wpos) = ctx
|
||||
.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_())
|
||||
})
|
||||
{
|
||||
if let Some(plaza_wpos) = choose_plaza(ctx, visiting_site) {
|
||||
// Walk to the plaza...
|
||||
Either::Left(travel_to_point(plaza_wpos, 0.5)
|
||||
.debug(|| "walk to plaza"))
|
||||
|
@ -464,6 +464,8 @@ fn set_owner_if_no_target(bdata: &mut BehaviorData) -> bool {
|
||||
false,
|
||||
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