1use aery::{prelude::*, tuple_traits::RelationEntries};
4use bevy::prelude::*;
5
6use prelude::*;
7use ron_loader::loader::TalksLoader;
8use traverse::{choice_handler, next_handler, set_has_started};
9
10pub mod actors;
11pub mod builder;
12pub mod errors;
13pub mod events;
14pub mod prelude;
15pub mod ron_loader;
16pub mod talk;
17pub mod talk_asset;
18mod traverse;
19
20pub struct TalksPlugin;
26
27impl Plugin for TalksPlugin {
28 fn build(&self, app: &mut App) {
29 if !app.is_plugin_added::<Aery>() {
30 app.add_plugins(Aery);
31 }
32
33 app.add_plugins(TalksEventsPlugin)
34 .register_asset_loader(TalksLoader)
35 .init_asset::<TalkData>()
36 .configure_sets(PreUpdate, TalksSet)
37 .add_systems(
38 PreUpdate,
39 (
40 next_handler.pipe(error_logger),
41 choice_handler.pipe(error_logger),
42 refire_handler.pipe(error_logger),
43 set_has_started.after(next_handler),
44 )
45 .in_set(TalksSet),
46 );
47 }
48}
49
50#[derive(SystemSet, Debug, Default, Clone, PartialEq, Eq, Hash)]
52struct TalksSet;
53
54fn error_logger(In(result): In<Result<(), NextActionError>>) {
56 if let Err(err) = result {
57 error!("Error: {err}");
58 }
59}
60
61fn refire_handler(
63 mut cmd: Commands,
64 mut reqs: EventReader<RefireNodeRequest>,
65 current_nodes: Query<(Entity, &Parent), With<CurrentNode>>,
66 start: Query<Entity, With<StartNode>>,
67 end: Query<Entity, With<EndNode>>,
68 all_actors: Query<&Actor>,
69 performers: Query<Relations<PerformedBy>>,
70 emitters: Query<&dyn NodeEventEmitter>,
71 type_registry: Res<AppTypeRegistry>,
72 mut start_ev_writer: EventWriter<StartEvent>,
73 mut end_ev_writer: EventWriter<EndEvent>,
74) -> Result<(), NextActionError> {
75 if let Some(event) = reqs.read().next() {
76 for (current_node, talk_parent) in ¤t_nodes {
77 let this_talk = talk_parent.get();
78 if this_talk == event.talk {
80 maybe_emit_start_event(&start, current_node, &mut start_ev_writer, event.talk);
82
83 maybe_emit_end_event(&end, current_node, &mut end_ev_writer, event.talk);
85
86 let actors_in_node = retrieve_actors(&performers, current_node, &all_actors);
88
89 emit_events(
91 &mut cmd,
92 &emitters,
93 current_node,
94 &type_registry,
95 actors_in_node,
96 );
97 return Ok(());
98 }
99 }
100 return Err(NextActionError::NoTalk);
101 }
102 Ok(())
103}
104
105#[inline]
107pub(crate) fn maybe_emit_start_event(
108 start: &Query<Entity, With<StartNode>>,
109 current_node: Entity,
110 start_ev_writer: &mut EventWriter<StartEvent>,
111 requested_talk: Entity,
112) {
113 if start.get(current_node).is_ok() {
114 start_ev_writer.send(StartEvent(requested_talk));
115 }
116}
117
118#[inline]
120pub(crate) fn maybe_emit_end_event(
121 end: &Query<Entity, With<EndNode>>,
122 next_node: Entity,
123 end_ev_writer: &mut EventWriter<EndEvent>,
124 requested_talk: Entity,
125) {
126 if end.get(next_node).is_ok() {
127 end_ev_writer.send(EndEvent(requested_talk));
128 }
129}
130
131#[inline]
133pub(crate) fn retrieve_actors(
134 performers: &Query<Relations<PerformedBy>>,
135 next_node: Entity,
136 all_actors: &Query<&Actor>,
137) -> Vec<Actor> {
138 let mut actors_in_node = Vec::<Actor>::new();
139 if let Ok(actor_edges) = &performers.get(next_node) {
140 for actor in actor_edges.targets(PerformedBy) {
141 actors_in_node.push(all_actors.get(*actor).expect("Actor").clone());
142 }
143 }
144 actors_in_node
145}
146
147#[inline]
149pub(crate) fn emit_events(
150 cmd: &mut Commands,
151 emitters: &Query<&dyn NodeEventEmitter>,
152 next_node: Entity,
153 type_registry: &Res<AppTypeRegistry>,
154 actors_in_node: Vec<Actor>,
155) {
156 if let Ok(emitters) = emitters.get(next_node) {
157 let type_registry = type_registry.read();
158
159 for emitter in &emitters {
160 let emitted_event = emitter.make(&actors_in_node);
161
162 let event_type_id = emitted_event.type_id();
163 let reflect_event = type_registry
166 .get_type_data::<ReflectEvent>(event_type_id)
167 .expect("Event not registered for event type")
168 .clone();
169
170 cmd.add(move |world: &mut World| {
171 reflect_event.send(&*emitted_event, world);
172 });
173 }
174 }
175}
176#[cfg(test)]
177mod tests {
178 use bevy::ecs::{
179 query::{ROQueryItem, WorldQuery},
180 system::Command,
181 };
182
183 use indexmap::indexmap;
184
185 use super::*;
186
187 pub fn talks_minimal_app() -> App {
189 let mut app = App::new();
190 app.add_plugins((AssetPlugin::default(), TalksPlugin));
191 app
192 }
193
194 #[inline]
195 #[track_caller]
196 pub fn get_comp<C: Component>(e: Entity, world: &mut World) -> &C {
197 world.entity(e).get::<C>().expect("Component")
198 }
199
200 #[inline]
201 #[track_caller]
202 pub fn count<Q: WorldQuery>(world: &mut World) -> usize {
203 world.query::<Q>().iter(&world).count()
204 }
205
206 #[inline]
207 #[track_caller]
208 pub fn single<Q: WorldQuery>(world: &mut World) -> ROQueryItem<Q> {
209 world.query::<Q>().single(world)
210 }
211
212 #[track_caller]
215 pub fn setup_and_next(talk_data: &TalkData) -> App {
216 let mut app = talks_minimal_app();
217 let builder = TalkBuilder::default().fill_with_talk_data(talk_data);
218 BuildTalkCommand::new(app.world.spawn(Talk::default()).id(), builder).apply(&mut app.world);
219 let (talk_ent, _) = single::<(Entity, With<Talk>)>(&mut app.world);
220 let (edges, _) = single::<(Relations<FollowedBy>, With<CurrentNode>)>(&mut app.world);
221
222 assert_eq!(edges.targets(FollowedBy).len(), 1);
223 let start_following_ent = edges.targets(FollowedBy)[0];
224
225 app.world.send_event(NextNodeRequest::new(talk_ent));
226 app.update();
227
228 let (next_e, _) = single::<(Entity, With<CurrentNode>)>(&mut app.world);
229 assert_eq!(next_e, start_following_ent);
230 app
231 }
232
233 #[test]
234 fn refire_request_sends_events() {
235 let script = indexmap! {
236 0 => Action { text: "Hello".to_string(), actors: vec!["actor_1".to_string()], ..default() }, };
238 let mut app = setup_and_next(&TalkData::new(script, vec![Actor::new("actor_1", "Actor")]));
239 let evs = app.world.resource::<Events<TextNodeEvent>>();
240 assert_eq!(evs.get_reader().read(evs).len(), 1);
241
242 let (talk_ent, _) = single::<(Entity, With<Talk>)>(&mut app.world);
243 app.world.send_event(RefireNodeRequest::new(talk_ent));
244 app.update();
245
246 let evs = app.world.resource::<Events<TextNodeEvent>>();
247 assert_eq!(evs.get_reader().read(evs).len(), 2);
248 }
249}