bevy_ecs/schedule/condition.rs
1use alloc::{boxed::Box, format};
2use bevy_utils::prelude::DebugName;
3use core::ops::Not;
4
5use crate::system::{
6 Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, RunSystemError,
7 System, SystemIn, SystemInput,
8};
9
10/// A type-erased run condition stored in a [`Box`].
11pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
12
13/// A system that determines if one or more scheduled systems should run.
14///
15/// Implemented for functions and closures that convert into [`System<Out=bool>`](System)
16/// with [read-only](crate::system::ReadOnlySystemParam) parameters.
17///
18/// # Marker type parameter
19///
20/// `SystemCondition` trait has `Marker` type parameter, which has no special meaning,
21/// but exists to work around the limitation of Rust's trait system.
22///
23/// Type parameter in return type can be set to `<()>` by calling [`IntoSystem::into_system`],
24/// but usually have to be specified when passing a condition to a function.
25///
26/// ```
27/// # use bevy_ecs::schedule::SystemCondition;
28/// # use bevy_ecs::system::IntoSystem;
29/// fn not_condition<Marker>(a: impl SystemCondition<Marker>) -> impl SystemCondition<()> {
30/// IntoSystem::into_system(a.map(|x| !x))
31/// }
32/// ```
33///
34/// # Examples
35/// A condition that returns true every other time it's called.
36/// ```
37/// # use bevy_ecs::prelude::*;
38/// fn every_other_time() -> impl SystemCondition<()> {
39/// IntoSystem::into_system(|mut flag: Local<bool>| {
40/// *flag = !*flag;
41/// *flag
42/// })
43/// }
44///
45/// # #[derive(Resource)] struct DidRun(bool);
46/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
47/// # let mut schedule = Schedule::default();
48/// schedule.add_systems(my_system.run_if(every_other_time()));
49/// # let mut world = World::new();
50/// # world.insert_resource(DidRun(false));
51/// # schedule.run(&mut world);
52/// # assert!(world.resource::<DidRun>().0);
53/// # world.insert_resource(DidRun(false));
54/// # schedule.run(&mut world);
55/// # assert!(!world.resource::<DidRun>().0);
56/// ```
57///
58/// A condition that takes a bool as an input and returns it unchanged.
59///
60/// ```
61/// # use bevy_ecs::prelude::*;
62/// fn identity() -> impl SystemCondition<(), In<bool>> {
63/// IntoSystem::into_system(|In(x): In<bool>| x)
64/// }
65///
66/// # fn always_true() -> bool { true }
67/// # let mut app = Schedule::default();
68/// # #[derive(Resource)] struct DidRun(bool);
69/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
70/// app.add_systems(my_system.run_if(always_true.pipe(identity())));
71/// # let mut world = World::new();
72/// # world.insert_resource(DidRun(false));
73/// # app.run(&mut world);
74/// # assert!(world.resource::<DidRun>().0);
75pub trait SystemCondition<Marker, In: SystemInput = ()>:
76 sealed::SystemCondition<Marker, In>
77{
78 /// Returns a new run condition that only returns `true`
79 /// if both this one and the passed `and` return `true`.
80 ///
81 /// The returned run condition is short-circuiting, meaning
82 /// `and` will only be invoked if `self` returns `true`.
83 ///
84 /// # Examples
85 ///
86 /// ```should_panic
87 /// use bevy_ecs::prelude::*;
88 ///
89 /// #[derive(Resource, PartialEq)]
90 /// struct R(u32);
91 ///
92 /// # let mut app = Schedule::default();
93 /// # let mut world = World::new();
94 /// # fn my_system() {}
95 /// app.add_systems(
96 /// // The `resource_equals` run condition will panic since we don't initialize `R`,
97 /// // just like if we used `Res<R>` in a system.
98 /// my_system.run_if(resource_equals(R(0))),
99 /// );
100 /// # app.run(&mut world);
101 /// ```
102 ///
103 /// Use `.and()` to avoid checking the condition.
104 ///
105 /// ```
106 /// # use bevy_ecs::prelude::*;
107 /// # #[derive(Resource, PartialEq)]
108 /// # struct R(u32);
109 /// # let mut app = Schedule::default();
110 /// # let mut world = World::new();
111 /// # fn my_system() {}
112 /// app.add_systems(
113 /// // `resource_equals` will only get run if the resource `R` exists.
114 /// my_system.run_if(resource_exists::<R>.and(resource_equals(R(0)))),
115 /// );
116 /// # app.run(&mut world);
117 /// ```
118 ///
119 /// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].
120 ///
121 /// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
122 fn and<M, C: SystemCondition<M, In>>(self, and: C) -> And<Self::System, C::System> {
123 let a = IntoSystem::into_system(self);
124 let b = IntoSystem::into_system(and);
125 let name = format!("{} && {}", a.name(), b.name());
126 CombinatorSystem::new(a, b, DebugName::owned(name))
127 }
128
129 /// Returns a new run condition that only returns `false`
130 /// if both this one and the passed `nand` return `true`.
131 ///
132 /// The returned run condition is short-circuiting, meaning
133 /// `nand` will only be invoked if `self` returns `true`.
134 ///
135 /// # Examples
136 ///
137 /// ```compile_fail
138 /// use bevy::prelude::*;
139 ///
140 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
141 /// pub enum PlayerState {
142 /// Alive,
143 /// Dead,
144 /// }
145 ///
146 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
147 /// pub enum EnemyState {
148 /// Alive,
149 /// Dead,
150 /// }
151 ///
152 /// # let mut app = Schedule::default();
153 /// # let mut world = World::new();
154 /// # fn game_over_credits() {}
155 /// app.add_systems(
156 /// // The game_over_credits system will only execute if either the `in_state(PlayerState::Alive)`
157 /// // run condition or `in_state(EnemyState::Alive)` run condition evaluates to `false`.
158 /// game_over_credits.run_if(
159 /// in_state(PlayerState::Alive).nand(in_state(EnemyState::Alive))
160 /// ),
161 /// );
162 /// # app.run(&mut world);
163 /// ```
164 ///
165 /// Equivalent logic can be achieved by using `not` in concert with `and`:
166 ///
167 /// ```compile_fail
168 /// app.add_systems(
169 /// game_over_credits.run_if(
170 /// not(in_state(PlayerState::Alive).and(in_state(EnemyState::Alive)))
171 /// ),
172 /// );
173 /// ```
174 fn nand<M, C: SystemCondition<M, In>>(self, nand: C) -> Nand<Self::System, C::System> {
175 let a = IntoSystem::into_system(self);
176 let b = IntoSystem::into_system(nand);
177 let name = format!("!({} && {})", a.name(), b.name());
178 CombinatorSystem::new(a, b, DebugName::owned(name))
179 }
180
181 /// Returns a new run condition that only returns `true`
182 /// if both this one and the passed `nor` return `false`.
183 ///
184 /// The returned run condition is short-circuiting, meaning
185 /// `nor` will only be invoked if `self` returns `false`.
186 ///
187 /// # Examples
188 ///
189 /// ```compile_fail
190 /// use bevy::prelude::*;
191 ///
192 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
193 /// pub enum WeatherState {
194 /// Sunny,
195 /// Cloudy,
196 /// }
197 ///
198 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
199 /// pub enum SoilState {
200 /// Fertilized,
201 /// NotFertilized,
202 /// }
203 ///
204 /// # let mut app = Schedule::default();
205 /// # let mut world = World::new();
206 /// # fn slow_plant_growth() {}
207 /// app.add_systems(
208 /// // The slow_plant_growth system will only execute if both the `in_state(WeatherState::Sunny)`
209 /// // run condition and `in_state(SoilState::Fertilized)` run condition evaluate to `false`.
210 /// slow_plant_growth.run_if(
211 /// in_state(WeatherState::Sunny).nor(in_state(SoilState::Fertilized))
212 /// ),
213 /// );
214 /// # app.run(&mut world);
215 /// ```
216 ///
217 /// Equivalent logic can be achieved by using `not` in concert with `or`:
218 ///
219 /// ```compile_fail
220 /// app.add_systems(
221 /// slow_plant_growth.run_if(
222 /// not(in_state(WeatherState::Sunny).or(in_state(SoilState::Fertilized)))
223 /// ),
224 /// );
225 /// ```
226 fn nor<M, C: SystemCondition<M, In>>(self, nor: C) -> Nor<Self::System, C::System> {
227 let a = IntoSystem::into_system(self);
228 let b = IntoSystem::into_system(nor);
229 let name = format!("!({} || {})", a.name(), b.name());
230 CombinatorSystem::new(a, b, DebugName::owned(name))
231 }
232
233 /// Returns a new run condition that returns `true`
234 /// if either this one or the passed `or` return `true`.
235 ///
236 /// The returned run condition is short-circuiting, meaning
237 /// `or` will only be invoked if `self` returns `false`.
238 ///
239 /// # Examples
240 ///
241 /// ```
242 /// use bevy_ecs::prelude::*;
243 ///
244 /// #[derive(Resource, PartialEq)]
245 /// struct A(u32);
246 ///
247 /// #[derive(Resource, PartialEq)]
248 /// struct B(u32);
249 ///
250 /// # let mut app = Schedule::default();
251 /// # let mut world = World::new();
252 /// # #[derive(Resource)] struct C(bool);
253 /// # fn my_system(mut c: ResMut<C>) { c.0 = true; }
254 /// app.add_systems(
255 /// // Only run the system if either `A` or `B` exist.
256 /// my_system.run_if(resource_exists::<A>.or(resource_exists::<B>)),
257 /// );
258 /// #
259 /// # world.insert_resource(C(false));
260 /// # app.run(&mut world);
261 /// # assert!(!world.resource::<C>().0);
262 /// #
263 /// # world.insert_resource(A(0));
264 /// # app.run(&mut world);
265 /// # assert!(world.resource::<C>().0);
266 /// #
267 /// # world.remove_resource::<A>();
268 /// # world.insert_resource(B(0));
269 /// # world.insert_resource(C(false));
270 /// # app.run(&mut world);
271 /// # assert!(world.resource::<C>().0);
272 /// ```
273 fn or<M, C: SystemCondition<M, In>>(self, or: C) -> Or<Self::System, C::System> {
274 let a = IntoSystem::into_system(self);
275 let b = IntoSystem::into_system(or);
276 let name = format!("{} || {}", a.name(), b.name());
277 CombinatorSystem::new(a, b, DebugName::owned(name))
278 }
279
280 /// Returns a new run condition that only returns `true`
281 /// if `self` and `xnor` **both** return `false` or **both** return `true`.
282 ///
283 /// # Examples
284 ///
285 /// ```compile_fail
286 /// use bevy::prelude::*;
287 ///
288 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
289 /// pub enum CoffeeMachineState {
290 /// Heating,
291 /// Brewing,
292 /// Inactive,
293 /// }
294 ///
295 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
296 /// pub enum TeaKettleState {
297 /// Heating,
298 /// Steeping,
299 /// Inactive,
300 /// }
301 ///
302 /// # let mut app = Schedule::default();
303 /// # let mut world = World::new();
304 /// # fn take_drink_orders() {}
305 /// app.add_systems(
306 /// // The take_drink_orders system will only execute if the `in_state(CoffeeMachineState::Inactive)`
307 /// // run condition and `in_state(TeaKettleState::Inactive)` run conditions both evaluate to `false`,
308 /// // or both evaluate to `true`.
309 /// take_drink_orders.run_if(
310 /// in_state(CoffeeMachineState::Inactive).xnor(in_state(TeaKettleState::Inactive))
311 /// ),
312 /// );
313 /// # app.run(&mut world);
314 /// ```
315 ///
316 /// Equivalent logic can be achieved by using `not` in concert with `xor`:
317 ///
318 /// ```compile_fail
319 /// app.add_systems(
320 /// take_drink_orders.run_if(
321 /// not(in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive)))
322 /// ),
323 /// );
324 /// ```
325 fn xnor<M, C: SystemCondition<M, In>>(self, xnor: C) -> Xnor<Self::System, C::System> {
326 let a = IntoSystem::into_system(self);
327 let b = IntoSystem::into_system(xnor);
328 let name = format!("!({} ^ {})", a.name(), b.name());
329 CombinatorSystem::new(a, b, DebugName::owned(name))
330 }
331
332 /// Returns a new run condition that only returns `true`
333 /// if either `self` or `xor` return `true`, but not both.
334 ///
335 /// # Examples
336 ///
337 /// ```compile_fail
338 /// use bevy::prelude::*;
339 ///
340 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
341 /// pub enum CoffeeMachineState {
342 /// Heating,
343 /// Brewing,
344 /// Inactive,
345 /// }
346 ///
347 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
348 /// pub enum TeaKettleState {
349 /// Heating,
350 /// Steeping,
351 /// Inactive,
352 /// }
353 ///
354 /// # let mut app = Schedule::default();
355 /// # let mut world = World::new();
356 /// # fn prepare_beverage() {}
357 /// app.add_systems(
358 /// // The prepare_beverage system will only execute if either the `in_state(CoffeeMachineState::Inactive)`
359 /// // run condition or `in_state(TeaKettleState::Inactive)` run condition evaluates to `true`,
360 /// // but not both.
361 /// prepare_beverage.run_if(
362 /// in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive))
363 /// ),
364 /// );
365 /// # app.run(&mut world);
366 /// ```
367 fn xor<M, C: SystemCondition<M, In>>(self, xor: C) -> Xor<Self::System, C::System> {
368 let a = IntoSystem::into_system(self);
369 let b = IntoSystem::into_system(xor);
370 let name = format!("({} ^ {})", a.name(), b.name());
371 CombinatorSystem::new(a, b, DebugName::owned(name))
372 }
373}
374
375impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F where
376 F: sealed::SystemCondition<Marker, In>
377{
378}
379
380mod sealed {
381 use crate::system::{IntoSystem, ReadOnlySystem, SystemInput};
382
383 pub trait SystemCondition<Marker, In: SystemInput>:
384 IntoSystem<In, bool, Marker, System = Self::ReadOnlySystem>
385 {
386 // This associated type is necessary to let the compiler
387 // know that `Self::System` is `ReadOnlySystem`.
388 type ReadOnlySystem: ReadOnlySystem<In = In, Out = bool>;
389 }
390
391 impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F
392 where
393 F: IntoSystem<In, bool, Marker>,
394 F::System: ReadOnlySystem,
395 {
396 type ReadOnlySystem = F::System;
397 }
398}
399
400/// A collection of [run conditions](SystemCondition) that may be useful in any bevy app.
401pub mod common_conditions {
402 use super::{NotSystem, SystemCondition};
403 use crate::{
404 change_detection::DetectChanges,
405 lifecycle::RemovedComponents,
406 message::{Message, MessageReader},
407 prelude::{Component, Query, With},
408 query::QueryFilter,
409 resource::Resource,
410 system::{In, IntoSystem, Local, Res, System, SystemInput},
411 };
412 use alloc::format;
413
414 /// A [`SystemCondition`]-satisfying system that returns `true`
415 /// on the first time the condition is run and false every time after.
416 ///
417 /// # Example
418 ///
419 /// ```
420 /// # use bevy_ecs::prelude::*;
421 /// # #[derive(Resource, Default)]
422 /// # struct Counter(u8);
423 /// # let mut app = Schedule::default();
424 /// # let mut world = World::new();
425 /// # world.init_resource::<Counter>();
426 /// app.add_systems(
427 /// // `run_once` will only return true the first time it's evaluated
428 /// my_system.run_if(run_once),
429 /// );
430 ///
431 /// fn my_system(mut counter: ResMut<Counter>) {
432 /// counter.0 += 1;
433 /// }
434 ///
435 /// // This is the first time the condition will be evaluated so `my_system` will run
436 /// app.run(&mut world);
437 /// assert_eq!(world.resource::<Counter>().0, 1);
438 ///
439 /// // This is the seconds time the condition will be evaluated so `my_system` won't run
440 /// app.run(&mut world);
441 /// assert_eq!(world.resource::<Counter>().0, 1);
442 /// ```
443 pub fn run_once(mut has_run: Local<bool>) -> bool {
444 if !*has_run {
445 *has_run = true;
446 true
447 } else {
448 false
449 }
450 }
451
452 /// A [`SystemCondition`]-satisfying system that returns `true`
453 /// if the resource exists.
454 ///
455 /// # Example
456 ///
457 /// ```
458 /// # use bevy_ecs::prelude::*;
459 /// # #[derive(Resource, Default)]
460 /// # struct Counter(u8);
461 /// # let mut app = Schedule::default();
462 /// # let mut world = World::new();
463 /// app.add_systems(
464 /// // `resource_exists` will only return true if the given resource exists in the world
465 /// my_system.run_if(resource_exists::<Counter>),
466 /// );
467 ///
468 /// fn my_system(mut counter: ResMut<Counter>) {
469 /// counter.0 += 1;
470 /// }
471 ///
472 /// // `Counter` hasn't been added so `my_system` won't run
473 /// app.run(&mut world);
474 /// world.init_resource::<Counter>();
475 ///
476 /// // `Counter` has now been added so `my_system` can run
477 /// app.run(&mut world);
478 /// assert_eq!(world.resource::<Counter>().0, 1);
479 /// ```
480 pub fn resource_exists<T>(res: Option<Res<T>>) -> bool
481 where
482 T: Resource,
483 {
484 res.is_some()
485 }
486
487 /// Generates a [`SystemCondition`]-satisfying closure that returns `true`
488 /// if the resource is equal to `value`.
489 ///
490 /// # Panics
491 ///
492 /// The condition will panic if the resource does not exist.
493 ///
494 /// # Example
495 ///
496 /// ```
497 /// # use bevy_ecs::prelude::*;
498 /// # #[derive(Resource, Default, PartialEq)]
499 /// # struct Counter(u8);
500 /// # let mut app = Schedule::default();
501 /// # let mut world = World::new();
502 /// # world.init_resource::<Counter>();
503 /// app.add_systems(
504 /// // `resource_equals` will only return true if the given resource equals the given value
505 /// my_system.run_if(resource_equals(Counter(0))),
506 /// );
507 ///
508 /// fn my_system(mut counter: ResMut<Counter>) {
509 /// counter.0 += 1;
510 /// }
511 ///
512 /// // `Counter` is `0` so `my_system` can run
513 /// app.run(&mut world);
514 /// assert_eq!(world.resource::<Counter>().0, 1);
515 ///
516 /// // `Counter` is no longer `0` so `my_system` won't run
517 /// app.run(&mut world);
518 /// assert_eq!(world.resource::<Counter>().0, 1);
519 /// ```
520 pub fn resource_equals<T>(value: T) -> impl FnMut(Res<T>) -> bool
521 where
522 T: Resource + PartialEq,
523 {
524 move |res: Res<T>| *res == value
525 }
526
527 /// Generates a [`SystemCondition`]-satisfying closure that returns `true`
528 /// if the resource exists and is equal to `value`.
529 ///
530 /// The condition will return `false` if the resource does not exist.
531 ///
532 /// # Example
533 ///
534 /// ```
535 /// # use bevy_ecs::prelude::*;
536 /// # #[derive(Resource, Default, PartialEq)]
537 /// # struct Counter(u8);
538 /// # let mut app = Schedule::default();
539 /// # let mut world = World::new();
540 /// app.add_systems(
541 /// // `resource_exists_and_equals` will only return true
542 /// // if the given resource exists and equals the given value
543 /// my_system.run_if(resource_exists_and_equals(Counter(0))),
544 /// );
545 ///
546 /// fn my_system(mut counter: ResMut<Counter>) {
547 /// counter.0 += 1;
548 /// }
549 ///
550 /// // `Counter` hasn't been added so `my_system` can't run
551 /// app.run(&mut world);
552 /// world.init_resource::<Counter>();
553 ///
554 /// // `Counter` is `0` so `my_system` can run
555 /// app.run(&mut world);
556 /// assert_eq!(world.resource::<Counter>().0, 1);
557 ///
558 /// // `Counter` is no longer `0` so `my_system` won't run
559 /// app.run(&mut world);
560 /// assert_eq!(world.resource::<Counter>().0, 1);
561 /// ```
562 pub fn resource_exists_and_equals<T>(value: T) -> impl FnMut(Option<Res<T>>) -> bool
563 where
564 T: Resource + PartialEq,
565 {
566 move |res: Option<Res<T>>| match res {
567 Some(res) => *res == value,
568 None => false,
569 }
570 }
571
572 /// A [`SystemCondition`]-satisfying system that returns `true`
573 /// if the resource of the given type has been added since the condition was last checked.
574 ///
575 /// # Example
576 ///
577 /// ```
578 /// # use bevy_ecs::prelude::*;
579 /// # #[derive(Resource, Default)]
580 /// # struct Counter(u8);
581 /// # let mut app = Schedule::default();
582 /// # let mut world = World::new();
583 /// app.add_systems(
584 /// // `resource_added` will only return true if the
585 /// // given resource was just added
586 /// my_system.run_if(resource_added::<Counter>),
587 /// );
588 ///
589 /// fn my_system(mut counter: ResMut<Counter>) {
590 /// counter.0 += 1;
591 /// }
592 ///
593 /// world.init_resource::<Counter>();
594 ///
595 /// // `Counter` was just added so `my_system` will run
596 /// app.run(&mut world);
597 /// assert_eq!(world.resource::<Counter>().0, 1);
598 ///
599 /// // `Counter` was not just added so `my_system` will not run
600 /// app.run(&mut world);
601 /// assert_eq!(world.resource::<Counter>().0, 1);
602 /// ```
603 pub fn resource_added<T>(res: Option<Res<T>>) -> bool
604 where
605 T: Resource,
606 {
607 match res {
608 Some(res) => res.is_added(),
609 None => false,
610 }
611 }
612
613 /// A [`SystemCondition`]-satisfying system that returns `true`
614 /// if the resource of the given type has been added or mutably dereferenced
615 /// since the condition was last checked.
616 ///
617 /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
618 /// Bevy does not compare resources to their previous values.
619 ///
620 /// # Panics
621 ///
622 /// The condition will panic if the resource does not exist.
623 ///
624 /// # Example
625 ///
626 /// ```
627 /// # use bevy_ecs::prelude::*;
628 /// # #[derive(Resource, Default)]
629 /// # struct Counter(u8);
630 /// # let mut app = Schedule::default();
631 /// # let mut world = World::new();
632 /// # world.init_resource::<Counter>();
633 /// app.add_systems(
634 /// // `resource_changed` will only return true if the
635 /// // given resource was just changed (or added)
636 /// my_system.run_if(
637 /// resource_changed::<Counter>
638 /// // By default detecting changes will also trigger if the resource was
639 /// // just added, this won't work with my example so I will add a second
640 /// // condition to make sure the resource wasn't just added
641 /// .and(not(resource_added::<Counter>))
642 /// ),
643 /// );
644 ///
645 /// fn my_system(mut counter: ResMut<Counter>) {
646 /// counter.0 += 1;
647 /// }
648 ///
649 /// // `Counter` hasn't been changed so `my_system` won't run
650 /// app.run(&mut world);
651 /// assert_eq!(world.resource::<Counter>().0, 0);
652 ///
653 /// world.resource_mut::<Counter>().0 = 50;
654 ///
655 /// // `Counter` was just changed so `my_system` will run
656 /// app.run(&mut world);
657 /// assert_eq!(world.resource::<Counter>().0, 51);
658 /// ```
659 pub fn resource_changed<T>(res: Res<T>) -> bool
660 where
661 T: Resource,
662 {
663 res.is_changed()
664 }
665
666 /// A [`SystemCondition`]-satisfying system that returns `true`
667 /// if the resource of the given type has been added or mutably dereferenced since the condition
668 /// was last checked.
669 ///
670 /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
671 /// Bevy does not compare resources to their previous values.
672 ///
673 /// The condition will return `false` if the resource does not exist.
674 ///
675 /// # Example
676 ///
677 /// ```
678 /// # use bevy_ecs::prelude::*;
679 /// # #[derive(Resource, Default)]
680 /// # struct Counter(u8);
681 /// # let mut app = Schedule::default();
682 /// # let mut world = World::new();
683 /// app.add_systems(
684 /// // `resource_exists_and_changed` will only return true if the
685 /// // given resource exists and was just changed (or added)
686 /// my_system.run_if(
687 /// resource_exists_and_changed::<Counter>
688 /// // By default detecting changes will also trigger if the resource was
689 /// // just added, this won't work with my example so I will add a second
690 /// // condition to make sure the resource wasn't just added
691 /// .and(not(resource_added::<Counter>))
692 /// ),
693 /// );
694 ///
695 /// fn my_system(mut counter: ResMut<Counter>) {
696 /// counter.0 += 1;
697 /// }
698 ///
699 /// // `Counter` doesn't exist so `my_system` won't run
700 /// app.run(&mut world);
701 /// world.init_resource::<Counter>();
702 ///
703 /// // `Counter` hasn't been changed so `my_system` won't run
704 /// app.run(&mut world);
705 /// assert_eq!(world.resource::<Counter>().0, 0);
706 ///
707 /// world.resource_mut::<Counter>().0 = 50;
708 ///
709 /// // `Counter` was just changed so `my_system` will run
710 /// app.run(&mut world);
711 /// assert_eq!(world.resource::<Counter>().0, 51);
712 /// ```
713 pub fn resource_exists_and_changed<T>(res: Option<Res<T>>) -> bool
714 where
715 T: Resource,
716 {
717 match res {
718 Some(res) => res.is_changed(),
719 None => false,
720 }
721 }
722
723 /// A [`SystemCondition`]-satisfying system that returns `true`
724 /// if the resource of the given type has been added, removed or mutably dereferenced since the condition
725 /// was last checked.
726 ///
727 /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
728 /// Bevy does not compare resources to their previous values.
729 ///
730 /// The condition will return `false` if the resource does not exist.
731 ///
732 /// # Example
733 ///
734 /// ```
735 /// # use bevy_ecs::prelude::*;
736 /// # #[derive(Resource, Default)]
737 /// # struct Counter(u8);
738 /// # let mut app = Schedule::default();
739 /// # let mut world = World::new();
740 /// # world.init_resource::<Counter>();
741 /// app.add_systems(
742 /// // `resource_changed_or_removed` will only return true if the
743 /// // given resource was just changed or removed (or added)
744 /// my_system.run_if(
745 /// resource_changed_or_removed::<Counter>
746 /// // By default detecting changes will also trigger if the resource was
747 /// // just added, this won't work with my example so I will add a second
748 /// // condition to make sure the resource wasn't just added
749 /// .and(not(resource_added::<Counter>))
750 /// ),
751 /// );
752 ///
753 /// #[derive(Resource, Default)]
754 /// struct MyResource;
755 ///
756 /// // If `Counter` exists, increment it, otherwise insert `MyResource`
757 /// fn my_system(mut commands: Commands, mut counter: Option<ResMut<Counter>>) {
758 /// if let Some(mut counter) = counter {
759 /// counter.0 += 1;
760 /// } else {
761 /// commands.init_resource::<MyResource>();
762 /// }
763 /// }
764 ///
765 /// // `Counter` hasn't been changed so `my_system` won't run
766 /// app.run(&mut world);
767 /// assert_eq!(world.resource::<Counter>().0, 0);
768 ///
769 /// world.resource_mut::<Counter>().0 = 50;
770 ///
771 /// // `Counter` was just changed so `my_system` will run
772 /// app.run(&mut world);
773 /// assert_eq!(world.resource::<Counter>().0, 51);
774 ///
775 /// world.remove_resource::<Counter>();
776 ///
777 /// // `Counter` was just removed so `my_system` will run
778 /// app.run(&mut world);
779 /// assert_eq!(world.contains_resource::<MyResource>(), true);
780 /// ```
781 pub fn resource_changed_or_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
782 where
783 T: Resource,
784 {
785 if let Some(value) = res {
786 *existed = true;
787 value.is_changed()
788 } else if *existed {
789 *existed = false;
790 true
791 } else {
792 false
793 }
794 }
795
796 /// A [`SystemCondition`]-satisfying system that returns `true`
797 /// if the resource of the given type has been removed since the condition was last checked.
798 ///
799 /// # Example
800 ///
801 /// ```
802 /// # use bevy_ecs::prelude::*;
803 /// # #[derive(Resource, Default)]
804 /// # struct Counter(u8);
805 /// # let mut app = Schedule::default();
806 /// # let mut world = World::new();
807 /// # world.init_resource::<Counter>();
808 /// app.add_systems(
809 /// // `resource_removed` will only return true if the
810 /// // given resource was just removed
811 /// my_system.run_if(resource_removed::<MyResource>),
812 /// );
813 ///
814 /// #[derive(Resource, Default)]
815 /// struct MyResource;
816 ///
817 /// fn my_system(mut counter: ResMut<Counter>) {
818 /// counter.0 += 1;
819 /// }
820 ///
821 /// world.init_resource::<MyResource>();
822 ///
823 /// // `MyResource` hasn't just been removed so `my_system` won't run
824 /// app.run(&mut world);
825 /// assert_eq!(world.resource::<Counter>().0, 0);
826 ///
827 /// world.remove_resource::<MyResource>();
828 ///
829 /// // `MyResource` was just removed so `my_system` will run
830 /// app.run(&mut world);
831 /// assert_eq!(world.resource::<Counter>().0, 1);
832 /// ```
833 pub fn resource_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
834 where
835 T: Resource,
836 {
837 if res.is_some() {
838 *existed = true;
839 false
840 } else if *existed {
841 *existed = false;
842 true
843 } else {
844 false
845 }
846 }
847
848 /// A [`SystemCondition`]-satisfying system that returns `true`
849 /// if there are any new events of the given type since it was last called.
850 ///
851 /// # Example
852 ///
853 /// ```
854 /// # use bevy_ecs::prelude::*;
855 /// # #[derive(Resource, Default)]
856 /// # struct Counter(u8);
857 /// # let mut app = Schedule::default();
858 /// # let mut world = World::new();
859 /// # world.init_resource::<Counter>();
860 /// # world.init_resource::<Messages<MyMessage>>();
861 /// # app.add_systems(bevy_ecs::message::message_update_system.before(my_system));
862 ///
863 /// app.add_systems(
864 /// my_system.run_if(on_message::<MyMessage>),
865 /// );
866 ///
867 /// #[derive(Message)]
868 /// struct MyMessage;
869 ///
870 /// fn my_system(mut counter: ResMut<Counter>) {
871 /// counter.0 += 1;
872 /// }
873 ///
874 /// // No new `MyMessage` events have been push so `my_system` won't run
875 /// app.run(&mut world);
876 /// assert_eq!(world.resource::<Counter>().0, 0);
877 ///
878 /// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);
879 ///
880 /// // A `MyMessage` event has been pushed so `my_system` will run
881 /// app.run(&mut world);
882 /// assert_eq!(world.resource::<Counter>().0, 1);
883 /// ```
884 #[deprecated(since = "0.17.0", note = "Use `on_message` instead.")]
885 pub fn on_event<T: Message>(reader: MessageReader<T>) -> bool {
886 on_message(reader)
887 }
888
889 /// A [`SystemCondition`]-satisfying system that returns `true`
890 /// if there are any new messages of the given type since it was last called.
891 ///
892 /// # Example
893 ///
894 /// ```
895 /// # use bevy_ecs::prelude::*;
896 /// # #[derive(Resource, Default)]
897 /// # struct Counter(u8);
898 /// # let mut app = Schedule::default();
899 /// # let mut world = World::new();
900 /// # world.init_resource::<Counter>();
901 /// # world.init_resource::<Messages<MyMessage>>();
902 /// # app.add_systems(bevy_ecs::message::message_update_system.before(my_system));
903 ///
904 /// app.add_systems(
905 /// my_system.run_if(on_message::<MyMessage>),
906 /// );
907 ///
908 /// #[derive(Message)]
909 /// struct MyMessage;
910 ///
911 /// fn my_system(mut counter: ResMut<Counter>) {
912 /// counter.0 += 1;
913 /// }
914 ///
915 /// // No new `MyMessage` messages have been push so `my_system` won't run
916 /// app.run(&mut world);
917 /// assert_eq!(world.resource::<Counter>().0, 0);
918 ///
919 /// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);
920 ///
921 /// // A `MyMessage` message has been pushed so `my_system` will run
922 /// app.run(&mut world);
923 /// assert_eq!(world.resource::<Counter>().0, 1);
924 /// ```
925 pub fn on_message<M: Message>(mut reader: MessageReader<M>) -> bool {
926 // The messages need to be consumed, so that there are no false positives on subsequent
927 // calls of the run condition. Simply checking `is_empty` would not be enough.
928 // PERF: note that `count` is efficient (not actually looping/iterating),
929 // due to Bevy having a specialized implementation for messages.
930 reader.read().count() > 0
931 }
932
933 /// A [`SystemCondition`]-satisfying system that returns `true`
934 /// if there are any entities with the given component type.
935 ///
936 /// # Example
937 ///
938 /// ```
939 /// # use bevy_ecs::prelude::*;
940 /// # #[derive(Resource, Default)]
941 /// # struct Counter(u8);
942 /// # let mut app = Schedule::default();
943 /// # let mut world = World::new();
944 /// # world.init_resource::<Counter>();
945 /// app.add_systems(
946 /// my_system.run_if(any_with_component::<MyComponent>),
947 /// );
948 ///
949 /// #[derive(Component)]
950 /// struct MyComponent;
951 ///
952 /// fn my_system(mut counter: ResMut<Counter>) {
953 /// counter.0 += 1;
954 /// }
955 ///
956 /// // No entities exist yet with a `MyComponent` component so `my_system` won't run
957 /// app.run(&mut world);
958 /// assert_eq!(world.resource::<Counter>().0, 0);
959 ///
960 /// world.spawn(MyComponent);
961 ///
962 /// // An entities with `MyComponent` now exists so `my_system` will run
963 /// app.run(&mut world);
964 /// assert_eq!(world.resource::<Counter>().0, 1);
965 /// ```
966 pub fn any_with_component<T: Component>(query: Query<(), With<T>>) -> bool {
967 !query.is_empty()
968 }
969
970 /// A [`SystemCondition`]-satisfying system that returns `true`
971 /// if there are any entity with a component of the given type removed.
972 pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {
973 // `RemovedComponents` based on events and therefore events need to be consumed,
974 // so that there are no false positives on subsequent calls of the run condition.
975 // Simply checking `is_empty` would not be enough.
976 // PERF: note that `count` is efficient (not actually looping/iterating),
977 // due to Bevy having a specialized implementation for events.
978 removals.read().count() > 0
979 }
980
981 /// A [`SystemCondition`]-satisfying system that returns `true`
982 /// if there are any entities that match the given [`QueryFilter`].
983 pub fn any_match_filter<F: QueryFilter>(query: Query<(), F>) -> bool {
984 !query.is_empty()
985 }
986
987 /// Generates a [`SystemCondition`] that inverses the result of passed one.
988 ///
989 /// # Example
990 ///
991 /// ```
992 /// # use bevy_ecs::prelude::*;
993 /// # #[derive(Resource, Default)]
994 /// # struct Counter(u8);
995 /// # let mut app = Schedule::default();
996 /// # let mut world = World::new();
997 /// # world.init_resource::<Counter>();
998 /// app.add_systems(
999 /// // `not` will inverse any condition you pass in.
1000 /// // Since the condition we choose always returns true
1001 /// // this system will never run
1002 /// my_system.run_if(not(always)),
1003 /// );
1004 ///
1005 /// fn my_system(mut counter: ResMut<Counter>) {
1006 /// counter.0 += 1;
1007 /// }
1008 ///
1009 /// fn always() -> bool {
1010 /// true
1011 /// }
1012 ///
1013 /// app.run(&mut world);
1014 /// assert_eq!(world.resource::<Counter>().0, 0);
1015 /// ```
1016 pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>
1017 where
1018 TOut: core::ops::Not,
1019 T: IntoSystem<(), TOut, Marker>,
1020 {
1021 let condition = IntoSystem::into_system(condition);
1022 let name = format!("!{}", condition.name());
1023 NotSystem::new(super::NotMarker, condition, name.into())
1024 }
1025
1026 /// Generates a [`SystemCondition`] that returns true when the passed one changes.
1027 ///
1028 /// The first time this is called, the passed condition is assumed to have been previously false.
1029 ///
1030 /// # Example
1031 ///
1032 /// ```
1033 /// # use bevy_ecs::prelude::*;
1034 /// # #[derive(Resource, Default)]
1035 /// # struct Counter(u8);
1036 /// # let mut app = Schedule::default();
1037 /// # let mut world = World::new();
1038 /// # world.init_resource::<Counter>();
1039 /// app.add_systems(
1040 /// my_system.run_if(condition_changed(resource_exists::<MyResource>)),
1041 /// );
1042 ///
1043 /// #[derive(Resource)]
1044 /// struct MyResource;
1045 ///
1046 /// fn my_system(mut counter: ResMut<Counter>) {
1047 /// counter.0 += 1;
1048 /// }
1049 ///
1050 /// // `MyResource` is initially there, the inner condition is true, the system runs once
1051 /// world.insert_resource(MyResource);
1052 /// app.run(&mut world);
1053 /// assert_eq!(world.resource::<Counter>().0, 1);
1054 /// app.run(&mut world);
1055 /// assert_eq!(world.resource::<Counter>().0, 1);
1056 ///
1057 /// // We remove `MyResource`, the inner condition is now false, the system runs one more time.
1058 /// world.remove_resource::<MyResource>();
1059 /// app.run(&mut world);
1060 /// assert_eq!(world.resource::<Counter>().0, 2);
1061 /// app.run(&mut world);
1062 /// assert_eq!(world.resource::<Counter>().0, 2);
1063 /// ```
1064 pub fn condition_changed<Marker, CIn, C>(condition: C) -> impl SystemCondition<(), CIn>
1065 where
1066 CIn: SystemInput,
1067 C: SystemCondition<Marker, CIn>,
1068 {
1069 IntoSystem::into_system(condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| {
1070 let changed = *prev != new;
1071 *prev = new;
1072 changed
1073 }))
1074 }
1075
1076 /// Generates a [`SystemCondition`] that returns true when the result of
1077 /// the passed one went from false to true since the last time this was called.
1078 ///
1079 /// The first time this is called, the passed condition is assumed to have been previously false.
1080 ///
1081 /// # Example
1082 ///
1083 /// ```
1084 /// # use bevy_ecs::prelude::*;
1085 /// # #[derive(Resource, Default)]
1086 /// # struct Counter(u8);
1087 /// # let mut app = Schedule::default();
1088 /// # let mut world = World::new();
1089 /// # world.init_resource::<Counter>();
1090 /// app.add_systems(
1091 /// my_system.run_if(condition_changed_to(true, resource_exists::<MyResource>)),
1092 /// );
1093 ///
1094 /// #[derive(Resource)]
1095 /// struct MyResource;
1096 ///
1097 /// fn my_system(mut counter: ResMut<Counter>) {
1098 /// counter.0 += 1;
1099 /// }
1100 ///
1101 /// // `MyResource` is initially there, the inner condition is true, the system runs once
1102 /// world.insert_resource(MyResource);
1103 /// app.run(&mut world);
1104 /// assert_eq!(world.resource::<Counter>().0, 1);
1105 /// app.run(&mut world);
1106 /// assert_eq!(world.resource::<Counter>().0, 1);
1107 ///
1108 /// // We remove `MyResource`, the inner condition is now false, the system doesn't run.
1109 /// world.remove_resource::<MyResource>();
1110 /// app.run(&mut world);
1111 /// assert_eq!(world.resource::<Counter>().0, 1);
1112 ///
1113 /// // We reinsert `MyResource` again, so the system will run one more time
1114 /// world.insert_resource(MyResource);
1115 /// app.run(&mut world);
1116 /// assert_eq!(world.resource::<Counter>().0, 2);
1117 /// app.run(&mut world);
1118 /// assert_eq!(world.resource::<Counter>().0, 2);
1119 /// ```
1120 pub fn condition_changed_to<Marker, CIn, C>(
1121 to: bool,
1122 condition: C,
1123 ) -> impl SystemCondition<(), CIn>
1124 where
1125 CIn: SystemInput,
1126 C: SystemCondition<Marker, CIn>,
1127 {
1128 IntoSystem::into_system(condition.pipe(
1129 move |In(new): In<bool>, mut prev: Local<bool>| -> bool {
1130 let now_true = *prev != new && new == to;
1131 *prev = new;
1132 now_true
1133 },
1134 ))
1135 }
1136}
1137
1138/// Invokes [`Not`] with the output of another system.
1139///
1140/// See [`common_conditions::not`] for examples.
1141pub type NotSystem<S> = AdapterSystem<NotMarker, S>;
1142
1143/// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator.
1144#[doc(hidden)]
1145#[derive(Clone, Copy)]
1146pub struct NotMarker;
1147
1148impl<S: System<Out: Not>> Adapt<S> for NotMarker {
1149 type In = S::In;
1150 type Out = <S::Out as Not>::Output;
1151
1152 fn adapt(
1153 &mut self,
1154 input: <Self::In as SystemInput>::Inner<'_>,
1155 run_system: impl FnOnce(SystemIn<'_, S>) -> Result<S::Out, RunSystemError>,
1156 ) -> Result<Self::Out, RunSystemError> {
1157 run_system(input).map(Not::not)
1158 }
1159}
1160
1161/// Combines the outputs of two systems using the `&&` operator.
1162pub type And<A, B> = CombinatorSystem<AndMarker, A, B>;
1163
1164/// Combines and inverts the outputs of two systems using the `&&` and `!` operators.
1165pub type Nand<A, B> = CombinatorSystem<NandMarker, A, B>;
1166
1167/// Combines and inverts the outputs of two systems using the `&&` and `!` operators.
1168pub type Nor<A, B> = CombinatorSystem<NorMarker, A, B>;
1169
1170/// Combines the outputs of two systems using the `||` operator.
1171pub type Or<A, B> = CombinatorSystem<OrMarker, A, B>;
1172
1173/// Combines and inverts the outputs of two systems using the `^` and `!` operators.
1174pub type Xnor<A, B> = CombinatorSystem<XnorMarker, A, B>;
1175
1176/// Combines the outputs of two systems using the `^` operator.
1177pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;
1178
1179#[doc(hidden)]
1180pub struct AndMarker;
1181
1182impl<In, A, B> Combine<A, B> for AndMarker
1183where
1184 for<'a> In: SystemInput<Inner<'a>: Copy>,
1185 A: System<In = In, Out = bool>,
1186 B: System<In = In, Out = bool>,
1187{
1188 type In = In;
1189 type Out = bool;
1190
1191 fn combine<T>(
1192 input: <Self::In as SystemInput>::Inner<'_>,
1193 data: &mut T,
1194 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1195 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1196 ) -> Result<Self::Out, RunSystemError> {
1197 Ok(a(input, data)? && b(input, data)?)
1198 }
1199}
1200
1201#[doc(hidden)]
1202pub struct NandMarker;
1203
1204impl<In, A, B> Combine<A, B> for NandMarker
1205where
1206 for<'a> In: SystemInput<Inner<'a>: Copy>,
1207 A: System<In = In, Out = bool>,
1208 B: System<In = In, Out = bool>,
1209{
1210 type In = In;
1211 type Out = bool;
1212
1213 fn combine<T>(
1214 input: <Self::In as SystemInput>::Inner<'_>,
1215 data: &mut T,
1216 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1217 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1218 ) -> Result<Self::Out, RunSystemError> {
1219 Ok(!(a(input, data)? && b(input, data)?))
1220 }
1221}
1222
1223#[doc(hidden)]
1224pub struct NorMarker;
1225
1226impl<In, A, B> Combine<A, B> for NorMarker
1227where
1228 for<'a> In: SystemInput<Inner<'a>: Copy>,
1229 A: System<In = In, Out = bool>,
1230 B: System<In = In, Out = bool>,
1231{
1232 type In = In;
1233 type Out = bool;
1234
1235 fn combine<T>(
1236 input: <Self::In as SystemInput>::Inner<'_>,
1237 data: &mut T,
1238 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1239 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1240 ) -> Result<Self::Out, RunSystemError> {
1241 Ok(!(a(input, data)? || b(input, data)?))
1242 }
1243}
1244
1245#[doc(hidden)]
1246pub struct OrMarker;
1247
1248impl<In, A, B> Combine<A, B> for OrMarker
1249where
1250 for<'a> In: SystemInput<Inner<'a>: Copy>,
1251 A: System<In = In, Out = bool>,
1252 B: System<In = In, Out = bool>,
1253{
1254 type In = In;
1255 type Out = bool;
1256
1257 fn combine<T>(
1258 input: <Self::In as SystemInput>::Inner<'_>,
1259 data: &mut T,
1260 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1261 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1262 ) -> Result<Self::Out, RunSystemError> {
1263 Ok(a(input, data)? || b(input, data)?)
1264 }
1265}
1266
1267#[doc(hidden)]
1268pub struct XnorMarker;
1269
1270impl<In, A, B> Combine<A, B> for XnorMarker
1271where
1272 for<'a> In: SystemInput<Inner<'a>: Copy>,
1273 A: System<In = In, Out = bool>,
1274 B: System<In = In, Out = bool>,
1275{
1276 type In = In;
1277 type Out = bool;
1278
1279 fn combine<T>(
1280 input: <Self::In as SystemInput>::Inner<'_>,
1281 data: &mut T,
1282 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1283 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1284 ) -> Result<Self::Out, RunSystemError> {
1285 Ok(!(a(input, data)? ^ b(input, data)?))
1286 }
1287}
1288
1289#[doc(hidden)]
1290pub struct XorMarker;
1291
1292impl<In, A, B> Combine<A, B> for XorMarker
1293where
1294 for<'a> In: SystemInput<Inner<'a>: Copy>,
1295 A: System<In = In, Out = bool>,
1296 B: System<In = In, Out = bool>,
1297{
1298 type In = In;
1299 type Out = bool;
1300
1301 fn combine<T>(
1302 input: <Self::In as SystemInput>::Inner<'_>,
1303 data: &mut T,
1304 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1305 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1306 ) -> Result<Self::Out, RunSystemError> {
1307 Ok(a(input, data)? ^ b(input, data)?)
1308 }
1309}
1310
1311#[cfg(test)]
1312mod tests {
1313 use super::{common_conditions::*, SystemCondition};
1314 use crate::error::{BevyError, DefaultErrorHandler, ErrorContext};
1315 use crate::{
1316 change_detection::ResMut,
1317 component::Component,
1318 message::Message,
1319 query::With,
1320 schedule::{IntoScheduleConfigs, Schedule},
1321 system::{IntoSystem, Local, Res, System},
1322 world::World,
1323 };
1324 use bevy_ecs_macros::{Resource, SystemSet};
1325
1326 #[derive(Resource, Default)]
1327 struct Counter(usize);
1328
1329 fn increment_counter(mut counter: ResMut<Counter>) {
1330 counter.0 += 1;
1331 }
1332
1333 fn double_counter(mut counter: ResMut<Counter>) {
1334 counter.0 *= 2;
1335 }
1336
1337 fn every_other_time(mut has_ran: Local<bool>) -> bool {
1338 *has_ran = !*has_ran;
1339 *has_ran
1340 }
1341
1342 #[test]
1343 fn run_condition() {
1344 let mut world = World::new();
1345 world.init_resource::<Counter>();
1346 let mut schedule = Schedule::default();
1347
1348 // Run every other cycle
1349 schedule.add_systems(increment_counter.run_if(every_other_time));
1350
1351 schedule.run(&mut world);
1352 schedule.run(&mut world);
1353 assert_eq!(world.resource::<Counter>().0, 1);
1354 schedule.run(&mut world);
1355 schedule.run(&mut world);
1356 assert_eq!(world.resource::<Counter>().0, 2);
1357
1358 // Run every other cycle opposite to the last one
1359 schedule.add_systems(increment_counter.run_if(not(every_other_time)));
1360
1361 schedule.run(&mut world);
1362 schedule.run(&mut world);
1363 assert_eq!(world.resource::<Counter>().0, 4);
1364 schedule.run(&mut world);
1365 schedule.run(&mut world);
1366 assert_eq!(world.resource::<Counter>().0, 6);
1367 }
1368
1369 #[test]
1370 fn run_condition_combinators() {
1371 let mut world = World::new();
1372 world.init_resource::<Counter>();
1373 let mut schedule = Schedule::default();
1374
1375 schedule.add_systems(
1376 (
1377 increment_counter.run_if(every_other_time.and(|| true)), // Run every odd cycle.
1378 increment_counter.run_if(every_other_time.nand(|| false)), // Always run.
1379 double_counter.run_if(every_other_time.nor(|| false)), // Run every even cycle.
1380 increment_counter.run_if(every_other_time.or(|| true)), // Always run.
1381 increment_counter.run_if(every_other_time.xnor(|| true)), // Run every odd cycle.
1382 double_counter.run_if(every_other_time.xnor(|| false)), // Run every even cycle.
1383 increment_counter.run_if(every_other_time.xor(|| false)), // Run every odd cycle.
1384 double_counter.run_if(every_other_time.xor(|| true)), // Run every even cycle.
1385 )
1386 .chain(),
1387 );
1388
1389 schedule.run(&mut world);
1390 assert_eq!(world.resource::<Counter>().0, 5);
1391 schedule.run(&mut world);
1392 assert_eq!(world.resource::<Counter>().0, 52);
1393 }
1394
1395 #[test]
1396 fn multiple_run_conditions() {
1397 let mut world = World::new();
1398 world.init_resource::<Counter>();
1399 let mut schedule = Schedule::default();
1400
1401 // Run every other cycle
1402 schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true));
1403 // Never run
1404 schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false));
1405
1406 schedule.run(&mut world);
1407 assert_eq!(world.resource::<Counter>().0, 1);
1408 schedule.run(&mut world);
1409 assert_eq!(world.resource::<Counter>().0, 1);
1410 }
1411
1412 #[test]
1413 fn multiple_run_conditions_is_and_operation() {
1414 let mut world = World::new();
1415 world.init_resource::<Counter>();
1416
1417 let mut schedule = Schedule::default();
1418
1419 // This should never run, if multiple run conditions worked
1420 // like an OR condition then it would always run
1421 schedule.add_systems(
1422 increment_counter
1423 .run_if(every_other_time)
1424 .run_if(not(every_other_time)),
1425 );
1426
1427 schedule.run(&mut world);
1428 assert_eq!(world.resource::<Counter>().0, 0);
1429 schedule.run(&mut world);
1430 assert_eq!(world.resource::<Counter>().0, 0);
1431 }
1432 #[derive(Component)]
1433 struct TestComponent;
1434
1435 #[derive(Message)]
1436 struct TestMessage;
1437
1438 #[derive(Resource)]
1439 struct TestResource(());
1440
1441 fn test_system() {}
1442
1443 // Ensure distributive_run_if compiles with the common conditions.
1444 #[test]
1445 fn distributive_run_if_compiles() {
1446 Schedule::default().add_systems(
1447 (test_system, test_system)
1448 .distributive_run_if(run_once)
1449 .distributive_run_if(resource_exists::<TestResource>)
1450 .distributive_run_if(resource_added::<TestResource>)
1451 .distributive_run_if(resource_changed::<TestResource>)
1452 .distributive_run_if(resource_exists_and_changed::<TestResource>)
1453 .distributive_run_if(resource_changed_or_removed::<TestResource>)
1454 .distributive_run_if(resource_removed::<TestResource>)
1455 .distributive_run_if(on_message::<TestMessage>)
1456 .distributive_run_if(any_with_component::<TestComponent>)
1457 .distributive_run_if(any_match_filter::<With<TestComponent>>)
1458 .distributive_run_if(not(run_once)),
1459 );
1460 }
1461
1462 #[test]
1463 fn run_if_error_contains_system() {
1464 let mut world = World::new();
1465 world.insert_resource(DefaultErrorHandler(my_error_handler));
1466
1467 #[derive(Resource)]
1468 struct MyResource;
1469
1470 fn condition(_res: Res<MyResource>) -> bool {
1471 true
1472 }
1473
1474 fn my_error_handler(_: BevyError, ctx: ErrorContext) {
1475 let a = IntoSystem::into_system(system_a);
1476 let b = IntoSystem::into_system(system_b);
1477 assert!(
1478 matches!(ctx, ErrorContext::RunCondition { system, on_set, .. } if (on_set && system == b.name()) || (!on_set && system == a.name()))
1479 );
1480 }
1481
1482 fn system_a() {}
1483 fn system_b() {}
1484
1485 let mut schedule = Schedule::default();
1486 schedule.add_systems(system_a.run_if(condition));
1487 schedule.run(&mut world);
1488
1489 #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
1490 struct Set;
1491
1492 let mut schedule = Schedule::default();
1493 schedule
1494 .add_systems((system_b,).in_set(Set))
1495 .configure_sets(Set.run_if(condition));
1496 schedule.run(&mut world);
1497 }
1498}