veloren_rtsim/rule/npc_ai/
util.rs1use super::*;
2
3pub fn site_name(ctx: &NpcCtx, site_id: impl Into<Option<SiteId>>) -> Option<String> {
4 let world_site = ctx.state.data().sites.get(site_id.into()?)?.world_site?;
5 Some(ctx.index.sites.get(world_site).name().to_string())
6}
7
8pub fn locate_actor(ctx: &NpcCtx, actor: Actor) -> Option<Vec3<f32>> {
9 match actor {
10 Actor::Npc(npc_id) => ctx.state.data().npcs.get(npc_id).map(|npc| npc.wpos),
11 Actor::Character(character_id) => ctx
12 .system_data
13 .id_maps
14 .character_entity(character_id)
15 .and_then(|c| ctx.system_data.positions.get(c))
16 .map(|p| p.0),
17 }
18}
19
20pub fn actor_exists(ctx: &NpcCtx, actor: Actor) -> bool {
21 match actor {
22 Actor::Npc(npc_id) => ctx.state.data().npcs.contains_key(npc_id),
23 Actor::Character(character_id) => ctx
24 .system_data
25 .id_maps
26 .character_entity(character_id)
27 .is_some(),
28 }
29}
30
31pub fn talk<S: State>(tgt: Actor) -> impl Action<S> + Clone {
32 just(move |ctx, _| ctx.controller.do_talk(tgt)).debug(|| "talking")
33}
34
35pub fn do_dialogue<S: State, T: Default + Clone + Send + Sync + 'static, A: Action<S, T>>(
36 tgt: Actor,
37 f: impl Fn(DialogueSession) -> A + Send + Sync + 'static,
38) -> impl Action<S, T> {
39 now(move |ctx, _| {
40 let session = ctx.controller.dialogue_start(tgt);
41 f(session)
42 .stop_if(move |ctx: &mut NpcCtx| {
44 let mut stop = false;
45 ctx.inbox.retain(|input| {
46 if let NpcInput::Dialogue(_, dialogue) = input
47 && dialogue.id == session.id
48 && let DialogueKind::End = dialogue.kind
49 {
50 stop = true;
51 false
52 } else {
53 true
54 }
55 });
56 stop
57 })
58 .and_then(move |x: Option<T>| just(move |ctx, _| {
59 ctx.controller.do_idle();
60 ctx.controller.dialogue_end(session);
61 x.clone().unwrap_or_default()
62 }))
63 })
64}
65
66impl DialogueSession {
67 pub fn ask_question<
72 S: State,
73 R: Into<Response>,
74 T: Default + Send + Sync + 'static,
75 A: Action<S, T>,
76 >(
77 self,
78 question: Content,
79 responses: impl IntoIterator<Item = (R, A)> + Send + Sync + 'static,
80 ) -> impl Action<S, T> {
81 let (responses, actions): (Vec<_>, Vec<_>) = responses
82 .into_iter()
83 .enumerate()
84 .map(|(idx, (r, a))| ((idx as u16, r.into()), a))
85 .unzip();
86
87 let actions_once = take_once::TakeOnce::new();
88 let _ = actions_once.store(actions);
89
90 now(move |ctx, _| {
91 let q_tag = ctx.controller.dialogue_question(
92 self,
93 question.clone(),
94 responses.iter().cloned(),
95 );
96 let responses = responses.clone();
97 until(move |ctx, _| {
98 let mut id = None;
99 ctx.inbox.retain(|input| {
100 if let NpcInput::Dialogue(_, dialogue) = input
101 && dialogue.id == self.id
103 && let DialogueKind::Response { tag, response_id, response, .. } = &dialogue.kind
104 && *tag == q_tag
106 && responses.iter().any(|(r_id, r)| r_id == response_id && r == response)
108 {
109 id = Some(*response_id);
110 false
111 } else {
112 true
113 }
114 });
115 match id {
116 None => ControlFlow::Continue(talk(self.target)),
118 Some(response_id) => ControlFlow::Break(response_id),
119 }
120 })
121 })
122 .and_then(move |response_id| talk(self.target).repeat().stop_if(timeout(0.5)).map(move |_, _| response_id))
124 .stop_if(timeout(60.0))
127 .and_then(move |resp: Option<u16>| {
128 if let Some(action) = resp.and_then(|resp| actions_once.take().unwrap().into_iter().nth(resp as usize)) {
129 action.map(|x, _| x).boxed()
130 } else {
131 idle().map(|_, _| Default::default()).boxed()
132 }
133 })
134 }
135
136 pub fn say_statement<S: State>(self, stmt: Content) -> impl Action<S> {
137 now(move |ctx, _| {
138 ctx.controller.dialogue_statement(self, stmt.clone());
139 idle()
140 })
141 .then(talk(self.target)
142 .repeat()
143 .stop_if(timeout(2.5)))
145 .map(|_, _| ())
146 }
147}