bevy_stat_query/
stream.rs

1use crate::{attribute::Attribute, stat::StatValuePair, QualifierFlag, QualifierQuery, Querier};
2#[allow(unused)]
3use bevy_ecs::component::Component;
4use bevy_ecs::{
5    component::Mutable,
6    entity::Entity,
7    hierarchy::Children,
8    query::QueryData,
9    relationship::RelationshipTarget,
10    system::{Query, StaticSystemParam, SystemParam},
11};
12
13/// An isolated item that provides stat modifiers to a stat query.
14#[allow(unused_variables)]
15pub trait StatStream {
16    type Qualifier: QualifierFlag;
17
18    fn stream_stat(
19        &self,
20        entity: Entity,
21        qualifier: &QualifierQuery<Self::Qualifier>,
22        stat_value: &mut StatValuePair,
23        querier: Querier<Self::Qualifier>,
24    ) {
25    }
26
27    fn stream_relation(
28        &self,
29        other: &Self,
30        entity: Entity,
31        target: Entity,
32        qualifier: &QualifierQuery<Self::Qualifier>,
33        stat_value: &mut StatValuePair,
34        querier: Querier<Self::Qualifier>,
35    ) {
36    }
37
38    fn has_attribute(&self, entity: Entity, attribute: Attribute) -> bool {
39        false
40    }
41}
42
43impl<T> StatStream for &T
44where
45    T: StatStream,
46{
47    type Qualifier = T::Qualifier;
48
49    fn stream_stat(
50        &self,
51        entity: Entity,
52        qualifier: &QualifierQuery<Self::Qualifier>,
53        stat_value: &mut StatValuePair,
54        querier: Querier<Self::Qualifier>,
55    ) {
56        T::stream_stat(self, entity, qualifier, stat_value, querier);
57    }
58
59    fn stream_relation(
60        &self,
61        other: &Self,
62        entity: Entity,
63        target: Entity,
64        qualifier: &QualifierQuery<Self::Qualifier>,
65        stat_value: &mut StatValuePair,
66        querier: Querier<Self::Qualifier>,
67    ) {
68        T::stream_relation(self, other, entity, target, qualifier, stat_value, querier);
69    }
70
71    fn has_attribute(&self, entity: Entity, attribute: Attribute) -> bool {
72        T::has_attribute(self, entity, attribute)
73    }
74}
75
76impl<A, B> StatStream for (A, B)
77where
78    A: StatStream,
79    B: StatStream<Qualifier = A::Qualifier>,
80{
81    type Qualifier = A::Qualifier;
82
83    fn stream_stat(
84        &self,
85        entity: Entity,
86        qualifier: &QualifierQuery<Self::Qualifier>,
87        stat_value: &mut StatValuePair,
88        querier: Querier<Self::Qualifier>,
89    ) {
90        self.0.stream_stat(entity, qualifier, stat_value, querier);
91        self.1.stream_stat(entity, qualifier, stat_value, querier);
92    }
93
94    fn stream_relation(
95        &self,
96        other: &Self,
97        entity: Entity,
98        target: Entity,
99        qualifier: &QualifierQuery<Self::Qualifier>,
100        stat_value: &mut StatValuePair,
101        querier: Querier<Self::Qualifier>,
102    ) {
103        self.0
104            .stream_relation(&other.0, entity, target, qualifier, stat_value, querier);
105        self.1
106            .stream_relation(&other.1, entity, target, qualifier, stat_value, querier);
107    }
108
109    fn has_attribute(&self, entity: Entity, attribute: Attribute) -> bool {
110        self.0.has_attribute(entity, attribute) || self.1.has_attribute(entity, attribute)
111    }
112}
113
114/// A set of [`Component`]s and external [`SystemParam`]s that provide
115/// stat modifiers for an [`Entity`].
116#[allow(unused_variables)]
117pub trait QueryStream: 'static {
118    type Qualifier: QualifierFlag;
119    type Query: QueryData + 'static;
120    type Context: SystemParam + 'static;
121
122    fn stream_stat(
123        query: <<Self::Query as QueryData>::ReadOnly as QueryData>::Item<'_>,
124        context: &<Self::Context as SystemParam>::Item<'_, '_>,
125        entity: Entity,
126        qualifier: &QualifierQuery<Self::Qualifier>,
127        stat_value: &mut StatValuePair,
128        querier: Querier<Self::Qualifier>,
129    ) {
130    }
131
132    fn stream_relation(
133        this: <<Self::Query as QueryData>::ReadOnly as QueryData>::Item<'_>,
134        other: <<Self::Query as QueryData>::ReadOnly as QueryData>::Item<'_>,
135        context: &<Self::Context as SystemParam>::Item<'_, '_>,
136        entity: Entity,
137        target: Entity,
138        qualifier: &QualifierQuery<Self::Qualifier>,
139        stat_value: &mut StatValuePair,
140        querier: Querier<Self::Qualifier>,
141    ) {
142    }
143
144    fn has_attribute(
145        query: <<Self::Query as QueryData>::ReadOnly as QueryData>::Item<'_>,
146        context: &<Self::Context as SystemParam>::Item<'_, '_>,
147        entity: Entity,
148        attribute: Attribute,
149    ) -> bool {
150        false
151    }
152}
153
154impl<T> QueryStream for T
155where
156    T: Component<Mutability = Mutable> + StatStream,
157{
158    type Qualifier = T::Qualifier;
159    type Query = &'static mut T;
160    type Context = ();
161
162    fn stream_stat(
163        query: &T,
164        _: &(),
165        entity: Entity,
166        qualifier: &QualifierQuery<T::Qualifier>,
167        stat_value: &mut StatValuePair,
168        querier: Querier<T::Qualifier>,
169    ) {
170        query.stream_stat(entity, qualifier, stat_value, querier);
171    }
172
173    fn stream_relation(
174        this: &T,
175        other: &T,
176        _: &(),
177        entity: Entity,
178        target: Entity,
179        qualifier: &QualifierQuery<T::Qualifier>,
180        stat_value: &mut StatValuePair,
181        querier: Querier<T::Qualifier>,
182    ) {
183        this.stream_relation(other, entity, target, qualifier, stat_value, querier);
184    }
185
186    fn has_attribute(query: &T, _: &(), entity: Entity, attribute: Attribute) -> bool {
187        query.has_attribute(entity, attribute)
188    }
189}
190
191/// [`SystemParam`] for querying a [`QueryStream`].
192#[derive(SystemParam)]
193pub struct StatQuery<'w, 's, T: QueryStream> {
194    pub query: Query<'w, 's, <<T as QueryStream>::Query as QueryData>::ReadOnly>,
195    pub context: StaticSystemParam<'w, 's, <T as QueryStream>::Context>,
196}
197
198/// [`SystemParam`] for querying a [`QueryStream`].
199///
200/// Unlike [`StatQuery`], [`StatQueryMut`]'s query portion uses a mutable query,
201/// this is useful if you want to query and modify at the same time.
202#[derive(SystemParam)]
203pub struct StatQueryMut<'w, 's, T: QueryStream> {
204    pub query: Query<'w, 's, <T as QueryStream>::Query>,
205    pub context: StaticSystemParam<'w, 's, <T as QueryStream>::Context>,
206}
207
208impl<T: QueryStream> StatStream for StatQuery<'_, '_, T> {
209    type Qualifier = T::Qualifier;
210
211    fn stream_stat(
212        &self,
213        entity: Entity,
214        qualifier: &QualifierQuery<Self::Qualifier>,
215        stat_value: &mut StatValuePair,
216        querier: Querier<Self::Qualifier>,
217    ) {
218        if let Ok(item) = self.query.get(entity) {
219            T::stream_stat(item, &self.context, entity, qualifier, stat_value, querier);
220        }
221    }
222
223    fn stream_relation(
224        &self,
225        _: &Self,
226        entity: Entity,
227        target: Entity,
228        qualifier: &QualifierQuery<Self::Qualifier>,
229        stat_value: &mut StatValuePair,
230        querier: Querier<Self::Qualifier>,
231    ) {
232        if let Ok([this, other]) = self.query.get_many([entity, target]) {
233            T::stream_relation(
234                this,
235                other,
236                &self.context,
237                entity,
238                target,
239                qualifier,
240                stat_value,
241                querier,
242            );
243        }
244    }
245
246    fn has_attribute(&self, entity: Entity, attribute: Attribute) -> bool {
247        if let Ok(item) = self.query.get(entity) {
248            T::has_attribute(item, &self.context, entity, attribute)
249        } else {
250            false
251        }
252    }
253}
254
255impl<T: QueryStream> StatStream for StatQueryMut<'_, '_, T> {
256    type Qualifier = T::Qualifier;
257
258    fn stream_stat(
259        &self,
260        entity: Entity,
261        qualifier: &QualifierQuery<Self::Qualifier>,
262        stat_value: &mut StatValuePair,
263        querier: Querier<Self::Qualifier>,
264    ) {
265        if let Ok(item) = self.query.get(entity) {
266            T::stream_stat(item, &self.context, entity, qualifier, stat_value, querier);
267        }
268    }
269
270    fn stream_relation(
271        &self,
272        _: &Self,
273        entity: Entity,
274        target: Entity,
275        qualifier: &QualifierQuery<Self::Qualifier>,
276        stat_value: &mut StatValuePair,
277        querier: Querier<Self::Qualifier>,
278    ) {
279        if let Ok([this, other]) = self.query.get_many([entity, target]) {
280            T::stream_relation(
281                this,
282                other,
283                &self.context,
284                entity,
285                target,
286                qualifier,
287                stat_value,
288                querier,
289            );
290        }
291    }
292
293    fn has_attribute(&self, entity: Entity, attribute: Attribute) -> bool {
294        if let Ok(item) = self.query.get(entity) {
295            T::has_attribute(item, &self.context, entity, attribute)
296        } else {
297            false
298        }
299    }
300}
301
302/// A component that references other entities, like [`Children`].
303pub trait EntityReference: Component + 'static {
304    fn iter_entities(&self) -> impl Iterator<Item = Entity>;
305}
306
307impl<T> EntityReference for T
308where
309    T: RelationshipTarget,
310{
311    fn iter_entities(&self) -> impl Iterator<Item = Entity> {
312        self.iter()
313    }
314}
315
316/// [`SystemParam`] for querying [`QueryStream`]s on entities referenced by a component like [`Children`].
317///
318/// `query_relation` implementation is disabled since the behavior is undefined.
319#[derive(SystemParam)]
320pub struct ChildQuery<'w, 's, T: QueryStream, C: EntityReference = Children> {
321    pub query: Query<'w, 's, <<T as QueryStream>::Query as QueryData>::ReadOnly>,
322    pub context: StaticSystemParam<'w, 's, <T as QueryStream>::Context>,
323    pub children: Query<'w, 's, &'static C>,
324}
325
326/// [`SystemParam`] for querying [`QueryStream`]s on entities referenced by a component like [`Children`].
327///
328/// `query_relation` implementation is disabled since the behavior is undefined.
329#[derive(SystemParam)]
330pub struct ChildQueryMut<'w, 's, T: QueryStream, C: EntityReference = Children> {
331    pub query: Query<'w, 's, <T as QueryStream>::Query>,
332    pub context: StaticSystemParam<'w, 's, <T as QueryStream>::Context>,
333    pub children: Query<'w, 's, &'static C>,
334}
335
336impl<T: QueryStream, C: EntityReference> StatStream for ChildQuery<'_, '_, T, C> {
337    type Qualifier = T::Qualifier;
338
339    fn stream_stat(
340        &self,
341        entity: Entity,
342        qualifier: &QualifierQuery<Self::Qualifier>,
343        stat_value: &mut StatValuePair,
344        querier: Querier<Self::Qualifier>,
345    ) {
346        if let Ok(children) = self.children.get(entity) {
347            for item in self.query.iter_many(children.iter_entities()) {
348                T::stream_stat(item, &self.context, entity, qualifier, stat_value, querier);
349            }
350        }
351    }
352
353    fn has_attribute(&self, entity: Entity, attribute: Attribute) -> bool {
354        if let Ok(children) = self.children.get(entity) {
355            for item in self.query.iter_many(children.iter_entities()) {
356                if T::has_attribute(item, &self.context, entity, attribute) {
357                    return true;
358                }
359            }
360        }
361        false
362    }
363}
364
365impl<T: QueryStream, C: EntityReference> StatStream for ChildQueryMut<'_, '_, T, C> {
366    type Qualifier = T::Qualifier;
367
368    fn stream_stat(
369        &self,
370        entity: Entity,
371        qualifier: &QualifierQuery<Self::Qualifier>,
372        stat_value: &mut StatValuePair,
373        querier: Querier<Self::Qualifier>,
374    ) {
375        if let Ok(children) = self.children.get(entity) {
376            for item in self.query.iter_many(children.iter_entities()) {
377                T::stream_stat(item, &self.context, entity, qualifier, stat_value, querier);
378            }
379        }
380    }
381
382    fn has_attribute(&self, entity: Entity, attribute: Attribute) -> bool {
383        if let Ok(children) = self.children.get(entity) {
384            for item in self.query.iter_many(children.iter_entities()) {
385                if T::has_attribute(item, &self.context, entity, attribute) {
386                    return true;
387                }
388            }
389        }
390        false
391    }
392}