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