spew/
plugin.rs

1use crate::events::{delay_spawn_events, ReadySpawnEvent, SpawnEvent};
2use crate::spawner::{Spawner, Spawners};
3use bevy::prelude::*;
4
5#[allow(clippy::needless_doctest_main)]
6/// A plugin that enables spawning objects of type `T` while providing data of type `D`.
7/// Using multiple combinations of `T` and `D` requires adding multiple instances of this plugin to an [`App`].
8/// If your spawn systems don't require any data, simply pass `()` as the `D` type.
9///
10/// # Example
11/// ```rust,ignore
12/// use spew::prelude::*;
13/// use bevy::prelude::*;
14///
15/// #[derive(Debug, Eq, PartialEq)]
16/// enum Object {
17///    Cube
18/// }
19///
20/// fn main() {
21///    App::new()
22///      .add_plugins(DefaultPlugins)
23///      .add_plugins(SpewPlugin::<Object, Transform>::default())
24///      .run();
25/// }
26/// ```
27pub struct SpewPlugin<T, D = ()>
28where
29    T: Eq + Send + Sync + 'static,
30    D: Send + Sync + 'static,
31{
32    _spawner_enum_type: std::marker::PhantomData<T>,
33    _data_type: std::marker::PhantomData<D>,
34}
35
36impl<T, D> Default for SpewPlugin<T, D>
37where
38    T: Eq + Send + Sync + 'static,
39    D: Send + Sync + 'static,
40{
41    fn default() -> Self {
42        Self {
43            _spawner_enum_type: std::marker::PhantomData,
44            _data_type: std::marker::PhantomData,
45        }
46    }
47}
48
49impl<T, D> Plugin for SpewPlugin<T, D>
50where
51    T: Eq + Send + Sync + 'static,
52    D: Send + Sync + 'static,
53{
54    fn build(&self, app: &mut App) {
55        app.add_event::<SpawnEvent<T, D>>()
56            .add_event::<ReadySpawnEvent<T, D>>()
57            .add_systems(Update, delay_spawn_events::<T, D>.in_set(SpewSystemSet));
58    }
59
60    fn is_unique(&self) -> bool {
61        false
62    }
63}
64
65/// The SystemSet that contains all spew systems.
66#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
67pub struct SpewSystemSet;
68
69/// A trait that allows adding spawners to an [`App`].
70/// Spawners are tuples of an object and a spawning function, e.g. `(Object::Cube, spawn_cube)`.
71/// A spawning function has the same signature as a bevy system function, where user provided data is passed as an `In<D>` parameter in the first position.
72///
73/// The spawner's combination of object enum and user data must have been registered with an own [`SpewPlugin`] beforehand.
74pub trait SpewApp {
75    /// Add a single spawner to the app.
76    ///
77    /// # Example
78    /// ```rust,ignore
79    /// use spew::prelude::*;
80    /// use bevy::prelude::*;
81    ///
82    /// #[derive(Debug, Eq, PartialEq)]
83    /// enum Object {
84    ///   Cube
85    /// }
86    ///
87    /// fn main() {
88    ///     App::new()
89    ///         .add_plugins(DefaultPlugins)
90    ///         .add_plugins(SpewPlugin::<Object, Transform>::default())
91    ///         .add_spawner((Object::Cube, spawn_cube))
92    ///         .run();
93    /// }
94    ///
95    /// fn spawn_cube(In(transform): In<Transform>, mut commands: Commands) {
96    ///    info!("Spawning cube at {}", transform.translation);
97    ///    commands.spawn((Name::new("Cube"), transform));
98    /// }
99    /// ```
100    fn add_spawner<T, D>(&mut self, spawner: T) -> &mut App
101    where
102        T: Spawner<D>;
103
104    /// Add multiple spawners to the app by providing them in a tuple.
105    ///
106    /// # Example
107    /// ```rust,ignore
108    /// use spew::prelude::*;
109    /// use bevy::prelude::*;
110    ///
111    /// #[derive(Debug, Eq, PartialEq)]
112    /// enum Object {
113    ///   Cube,
114    ///   Triangle,
115    ///   Sphere,
116    /// }
117    ///
118    /// fn main() {
119    ///     App::new()
120    ///         .add_plugins(DefaultPlugins)
121    ///         .add_plugins(SpewPlugin::<Object, Transform>::default())
122    ///         .add_spawners((
123    ///             (Object::Cube, spawn_cube),
124    ///             (Object::Triangle, spawn_triangle),
125    ///             (Object::Sphere, spawn_sphere),
126    ///         ))
127    ///         .run();
128    /// }
129    ///
130    /// fn spawn_cube(In(transform): In<Transform>, mut commands: Commands) {
131    ///    info!("Spawning cube at {}", transform.translation);
132    ///    commands.spawn((Name::new("Cube"), transform));
133    /// }
134    ///
135    /// fn spawn_triangle(In(transform): In<Transform>, mut commands: Commands) {
136    ///    info!("Spawning triangle at {}", transform.translation);
137    ///    commands.spawn((Name::new("Triangle"), transform));
138    /// }
139    ///
140    /// fn spawn_sphere(In(transform): In<Transform>, mut commands: Commands) {
141    ///    info!("Spawning sphere at {}", transform.translation);
142    ///    commands.spawn((Name::new("Sphere"), transform));
143    /// }
144    /// ```
145    fn add_spawners<T, D>(&mut self, spawners: T) -> &mut App
146    where
147        T: Spawners<D>;
148}
149
150impl SpewApp for App {
151    fn add_spawner<T, D>(&mut self, spawner: T) -> &mut App
152    where
153        T: Spawner<D>,
154    {
155        spawner.add_to_app(self);
156        self
157    }
158    fn add_spawners<T, D>(&mut self, spawners: T) -> &mut App
159    where
160        T: Spawners<D>,
161    {
162        spawners.add_to_app(self);
163        self
164    }
165}