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 schedule = Schedule::default();
68/// # #[derive(Resource)] struct DidRun(bool);
69/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
70/// schedule.add_systems(my_system.run_if(always_true.pipe(identity())));
71/// # let mut world = World::new();
72/// # world.insert_resource(DidRun(false));
73/// # schedule.run(&mut world);
74/// # assert!(world.resource::<DidRun>().0);
75pub trait SystemCondition<Marker, In: SystemInput = ()>:
76 IntoSystem<In, bool, Marker, System: ReadOnlySystem>
77{
78 /// Returns a new run condition that only returns `true`
79 /// if both this one and the passed `then_run` return `true`.
80 ///
81 /// The returned run condition is short-circuiting, meaning
82 /// `then_run` will only be invoked if `self` returns `true`.
83 ///
84 /// Short-circuiting may not be desired in all cases; when utilizing change detection,
85 /// the `then_run` condition will react to changes since the last time that _`self` returned `true`_,
86 /// which may introduce subtle inconsistencies if short-circuiting was not intended. Similar issues
87 /// may arise for run conditions that rely on internal state, such as those using [`Local<T>`] parameters
88 /// or [`MessageReader<T>`], as they may not be updated every time the combined condition is evaluated.
89 ///
90 /// [`Local<T>`]: crate::system::Local
91 /// [`MessageReader<T>`]: crate::message::MessageReader
92 ///
93 /// See also [`and_eager`], which always evaluates both conditions.
94 ///
95 /// [`and_eager`]: SystemCondition::and_eager
96 ///
97 /// # Examples
98 ///
99 /// ```should_panic
100 /// use bevy_ecs::prelude::*;
101 ///
102 /// #[derive(Resource, PartialEq)]
103 /// struct R(u32);
104 ///
105 /// # let mut schedule = Schedule::default();
106 /// # let mut world = World::new();
107 /// # fn my_system() {}
108 /// schedule.add_systems(
109 /// // The `resource_equals` run condition will panic since we don't initialize `R`,
110 /// // just like if we used `Res<R>` in a system.
111 /// my_system.run_if(resource_equals(R(0))),
112 /// );
113 /// # schedule.run(&mut world);
114 /// ```
115 ///
116 /// Use `.and_then()` to avoid checking the condition.
117 ///
118 /// ```
119 /// # use bevy_ecs::prelude::*;
120 /// # #[derive(Resource, PartialEq)]
121 /// # struct R(u32);
122 /// # let mut schedule = Schedule::default();
123 /// # let mut world = World::new();
124 /// # fn my_system() { unreachable!() }
125 /// schedule.add_systems(
126 /// // `resource_equals` will only get run if the resource `R` exists.
127 /// my_system.run_if(resource_exists::<R>.and_then(resource_equals(R(0)))),
128 /// );
129 /// # schedule.run(&mut world);
130 /// ```
131 ///
132 /// Note that in this specific case, it's better to just use the run condition [`resource_exists_and_equals`].
133 ///
134 /// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
135 fn and_then<M, C: SystemCondition<M, In>>(
136 self,
137 then_run: C,
138 ) -> AndThen<Self::System, C::System> {
139 let a = IntoSystem::into_system(self);
140 let b = IntoSystem::into_system(then_run);
141 let name = format!("{} && {}", a.name(), b.name());
142 CombinatorSystem::new(a, b, DebugName::owned(name))
143 }
144
145 /// Returns a new run condition that only returns `true`
146 /// if both this one and the passed `then_run` return `true`.
147 ///
148 /// The returned run condition is eagerly evaluated, meaning
149 /// it will always execute both run conditions in order.
150 ///
151 /// When applied directly to a system using [`run_if`], the use of this combinator
152 /// is behaviorally identical to simply calling `run_if` multiple times. However,
153 /// `.and_eager` may be more efficient, as it does not erase the types of the inner conditions
154 /// when evaluating them, which may allow for compiler optimizations that are not possible
155 /// with separate calls to `run_if`.
156 ///
157 /// See also [`and_then`], which short-circuits if `self` returns `false`.
158 ///
159 /// [`run_if`]: crate::schedule::IntoScheduleConfigs::run_if
160 /// [`and_then`]: SystemCondition::and_then
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// # use bevy_ecs::prelude::*;
166 /// # use std::sync::atomic::AtomicBool;
167 /// # use std::sync::atomic::Ordering;
168 /// # #[derive(Resource, PartialEq)]
169 /// # struct R(u32);
170 /// # let mut schedule = Schedule::default();
171 /// # let mut world = World::new();
172 /// # fn my_system() { unreachable!() }
173 /// # static CONDITION_A_RAN: AtomicBool = AtomicBool::new(false);
174 /// # static CONDITION_B_RAN: AtomicBool = AtomicBool::new(false);
175 /// # fn returns_false() -> bool {
176 /// # CONDITION_A_RAN.store(true, Ordering::Relaxed);
177 /// # false
178 /// # }
179 /// # fn returns_true() -> bool {
180 /// # CONDITION_B_RAN.store(true, Ordering::Relaxed);
181 /// # true
182 /// # }
183 /// schedule.add_systems(
184 /// // both conditions will execute, even though the first one returned false
185 /// my_system.run_if(returns_false.and_eager(returns_true)),
186 /// );
187 /// # schedule.run(&mut world);
188 /// # assert!(CONDITION_A_RAN.load(Ordering::Relaxed));
189 /// # assert!(CONDITION_B_RAN.load(Ordering::Relaxed));
190 /// ```
191 fn and_eager<M, C: SystemCondition<M, In>>(
192 self,
193 other: C,
194 ) -> AndEager<Self::System, C::System> {
195 let a = IntoSystem::into_system(self);
196 let b = IntoSystem::into_system(other);
197 let name = format!("{} & {}", a.name(), b.name());
198 CombinatorSystem::new(a, b, DebugName::owned(name))
199 }
200
201 /// Returns a new run condition that only returns `false`
202 /// if both this one and the passed `then_run` return `true`.
203 ///
204 /// The returned run condition is short-circuiting, meaning
205 /// `then_run` will only be invoked if `self` returns `true`.
206 ///
207 /// Short-circuiting may not be desired in all cases; when utilizing change detection,
208 /// the `then_run` condition will react to changes since the last time that _`self` returned `true`_,
209 /// which may introduce subtle inconsistencies if short-circuiting was not intended. Similar issues
210 /// may arise for run conditions that rely on internal state, such as those using [`Local<T>`] parameters
211 /// or [`MessageReader<T>`], as they may not be updated every time the combined condition is evaluated.
212 ///
213 /// [`Local<T>`]: crate::system::Local
214 /// [`MessageReader<T>`]: crate::message::MessageReader
215 ///
216 /// See also [`nand_eager`], which always evaluates both conditions.
217 ///
218 /// [`nand_eager`]: SystemCondition::nand_eager
219 ///
220 /// # Examples
221 ///
222 /// ```
223 /// # use bevy_ecs::prelude::*;
224 /// #
225 /// # #[derive(Resource, Debug, Clone, PartialEq, Eq, Hash)]
226 /// # pub enum PlayerState {
227 /// # Alive,
228 /// # Dead,
229 /// # }
230 /// # #[derive(Resource, Debug, Clone, PartialEq, Eq, Hash)]
231 /// # pub enum EnemyState {
232 /// # Alive,
233 /// # Dead,
234 /// # }
235 /// #
236 /// # use std::sync::atomic::AtomicUsize;
237 /// # use std::sync::atomic::Ordering;
238 /// # static IN_STATE_RUN_COUNT: AtomicUsize = AtomicUsize::new(0);
239 /// # fn in_state<R: Resource + PartialEq>(state: R) -> impl Fn(Res<R>) -> bool {
240 /// # move |current_state| {
241 /// # IN_STATE_RUN_COUNT.fetch_add(1, Ordering::Relaxed);
242 /// # state == *current_state
243 /// # }
244 /// # }
245 /// #
246 /// # #[derive(Resource)]
247 /// # struct RanGameOver(bool);
248 /// # fn game_over_credits(mut commands: Commands) { commands.insert_resource(RanGameOver(true)); }
249 /// # let mut schedule = Schedule::default();
250 /// # let mut world = World::new();
251 /// # world.insert_resource(PlayerState::Dead);
252 /// schedule.add_systems(
253 /// // The game_over_credits system will only execute if either the `in_state(PlayerState::Alive)`
254 /// // run condition or `in_state(EnemyState::Alive)` run condition evaluates to `false`.
255 /// game_over_credits.run_if(
256 /// in_state(PlayerState::Alive).nand_then(in_state(EnemyState::Alive)),
257 /// ),
258 /// );
259 /// # schedule.run(&mut world);
260 /// # assert_eq!(IN_STATE_RUN_COUNT.load(Ordering::Relaxed), 1);
261 /// # assert!(world.resource::<RanGameOver>().0);
262 /// # IN_STATE_RUN_COUNT.store(0, Ordering::Relaxed);
263 /// # world.insert_resource(RanGameOver(false));
264 /// # world.insert_resource(PlayerState::Alive);
265 /// # world.insert_resource(EnemyState::Dead);
266 /// # schedule.run(&mut world);
267 /// # assert_eq!(IN_STATE_RUN_COUNT.load(Ordering::Relaxed), 2);
268 /// # assert!(world.resource::<RanGameOver>().0);
269 /// # IN_STATE_RUN_COUNT.store(0, Ordering::Relaxed);
270 /// # world.insert_resource(RanGameOver(false));
271 /// # world.insert_resource(EnemyState::Alive);
272 /// # schedule.run(&mut world);
273 /// # assert_eq!(IN_STATE_RUN_COUNT.load(Ordering::Relaxed), 2);
274 /// # assert!(!world.resource::<RanGameOver>().0);
275 /// ```
276 ///
277 /// Equivalent logic can be achieved by using `not` in concert with `and_then`:
278 ///
279 /// ```
280 /// # use bevy_ecs::prelude::*;
281 /// # #[derive(Resource, Debug, Clone, PartialEq, Eq, Hash)]
282 /// # pub enum PlayerState {
283 /// # Alive,
284 /// # Dead,
285 /// # }
286 /// # #[derive(Resource, Debug, Clone, PartialEq, Eq, Hash)]
287 /// # pub enum EnemyState {
288 /// # Alive,
289 /// # Dead,
290 /// # }
291 /// # fn in_state<R: Resource + PartialEq>(state: R) -> impl Fn(Res<R>) -> bool {
292 /// # move |current_state| state == *current_state
293 /// # }
294 /// # fn game_over_credits() { unreachable!() }
295 /// # let mut schedule = Schedule::default();
296 /// # let mut world = World::new();
297 /// # world.insert_resource(PlayerState::Alive);
298 /// # world.insert_resource(EnemyState::Alive);
299 /// schedule.add_systems(
300 /// game_over_credits.run_if(
301 /// not(in_state(PlayerState::Alive).and_then(in_state(EnemyState::Alive))),
302 /// ),
303 /// );
304 /// # schedule.run(&mut world);
305 /// ```
306 fn nand_then<M, C: SystemCondition<M, In>>(
307 self,
308 then_run: C,
309 ) -> NandThen<Self::System, C::System> {
310 let a = IntoSystem::into_system(self);
311 let b = IntoSystem::into_system(then_run);
312 let name = format!("!({} && {})", a.name(), b.name());
313 CombinatorSystem::new(a, b, DebugName::owned(name))
314 }
315
316 /// Returns a new run condition that only returns `false`
317 /// if both this one and the passed `then_run` return `true`.
318 ///
319 /// The returned run condition is eagerly evaluated, meaning
320 /// it will always execute both run conditions in order.
321 ///
322 /// See also [`nand_then`], which short-circuits if `self` returns `false`.
323 ///
324 /// [`nand_then`]: SystemCondition::nand_then
325 fn nand_eager<M, C: SystemCondition<M, In>>(
326 self,
327 other: C,
328 ) -> NandEager<Self::System, C::System> {
329 let a = IntoSystem::into_system(self);
330 let b = IntoSystem::into_system(other);
331 let name = format!("!({} & {})", a.name(), b.name());
332 CombinatorSystem::new(a, b, DebugName::owned(name))
333 }
334
335 /// Returns a new run condition that only returns `true`
336 /// if both this one and the passed `else_run` return `false`.
337 ///
338 /// The returned run condition is short-circuiting, meaning
339 /// `else_run` will only be invoked if `self` returns `true`.
340 ///
341 /// Short-circuiting may not be desired in all cases; when utilizing change detection,
342 /// the `else_run` condition will react to changes since the last time that _`self` returned `true`_,
343 /// which may introduce subtle inconsistencies if short-circuiting was not intended. Similar issues
344 /// may arise for run conditions that rely on internal state, such as those using [`Local<T>`] parameters
345 /// or [`MessageReader<T>`], as they may not be updated every time the combined condition is evaluated.
346 ///
347 /// [`Local<T>`]: crate::system::Local
348 /// [`MessageReader<T>`]: crate::message::MessageReader
349 ///
350 /// See also [`nor_eager`], which always evaluates both conditions.
351 ///
352 /// [`nor_eager`]: SystemCondition::nor_eager
353 ///
354 /// # Examples
355 ///
356 /// ```compile_fail
357 /// use bevy::prelude::*;
358 ///
359 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
360 /// pub enum WeatherState {
361 /// Sunny,
362 /// Cloudy,
363 /// }
364 ///
365 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
366 /// pub enum SoilState {
367 /// Fertilized,
368 /// NotFertilized,
369 /// }
370 ///
371 /// # let mut schedule = Schedule::default();
372 /// # let mut world = World::new();
373 /// # fn slow_plant_growth() {}
374 /// schedule.add_systems(
375 /// // The slow_plant_growth system will only execute if both the `in_state(WeatherState::Sunny)`
376 /// // run condition and `in_state(SoilState::Fertilized)` run condition evaluate to `false`.
377 /// slow_plant_growth.run_if(
378 /// in_state(WeatherState::Sunny).nor_else(in_state(SoilState::Fertilized)),
379 /// ),
380 /// );
381 /// # schedule.run(&mut world);
382 /// ```
383 ///
384 /// Equivalent logic can be achieved by using `not` in concert with `or`:
385 ///
386 /// ```compile_fail
387 /// schedule.add_systems(
388 /// slow_plant_growth.run_if(
389 /// not(in_state(WeatherState::Sunny).or_else(in_state(SoilState::Fertilized))),
390 /// ),
391 /// );
392 /// ```
393 fn nor_else<M, C: SystemCondition<M, In>>(
394 self,
395 else_run: C,
396 ) -> NorElse<Self::System, C::System> {
397 let a = IntoSystem::into_system(self);
398 let b = IntoSystem::into_system(else_run);
399 let name = format!("!({} || {})", a.name(), b.name());
400 CombinatorSystem::new(a, b, DebugName::owned(name))
401 }
402
403 /// Returns a new run condition that only returns `true`
404 /// if both this one and the passed `else_run` return `false`.
405 ///
406 /// The returned run condition is eagerly evaluated, meaning
407 /// it will always execute both run conditions in order.
408 ///
409 /// See also [`nor_else`], which short-circuits if `self` returns `true`.
410 ///
411 /// [`nor_else`]: SystemCondition::nor_else
412 fn nor_eager<M, C: SystemCondition<M, In>>(
413 self,
414 other: C,
415 ) -> NorEager<Self::System, C::System> {
416 let a = IntoSystem::into_system(self);
417 let b = IntoSystem::into_system(other);
418 let name = format!("!({} | {})", a.name(), b.name());
419 CombinatorSystem::new(a, b, DebugName::owned(name))
420 }
421
422 /// Returns a new run condition that returns `true`
423 /// if either this one or the passed `else_run` return `true`.
424 ///
425 /// The returned run condition is short-circuiting, meaning
426 /// `else_run` will only be invoked if `self` returns `false`.
427 ///
428 /// Short-circuiting may not be desired in all cases; when utilizing change detection,
429 /// the `else_run` condition will react to changes since the last time that _`self` returned `false`_,
430 /// which may introduce subtle inconsistencies if short-circuiting was not intended. Similar issues
431 /// may arise for run conditions that rely on internal state, such as those using [`Local<T>`] parameters
432 /// or [`MessageReader<T>`], as they may not be updated every time the combined condition is evaluated.
433 ///
434 /// [`Local<T>`]: crate::system::Local
435 /// [`MessageReader<T>`]: crate::message::MessageReader
436 ///
437 /// See also [`or_eager`], which always evaluates both conditions.
438 ///
439 /// [`or_eager`]: SystemCondition::or_eager
440 ///
441 /// # Examples
442 ///
443 /// ```
444 /// use bevy_ecs::prelude::*;
445 ///
446 /// #[derive(Resource, PartialEq)]
447 /// struct A(u32);
448 ///
449 /// #[derive(Resource, PartialEq)]
450 /// struct B(u32);
451 ///
452 /// # let mut schedule = Schedule::default();
453 /// # let mut world = World::new();
454 /// # #[derive(Resource)] struct C(bool);
455 /// # fn my_system(mut c: ResMut<C>) { c.0 = true; }
456 /// schedule.add_systems(
457 /// // Only run the system if either `A` or else `B` exist.
458 /// my_system.run_if(resource_exists::<A>.or_else(resource_exists::<B>)),
459 /// );
460 /// #
461 /// # world.insert_resource(C(false));
462 /// # schedule.run(&mut world);
463 /// # assert!(!world.resource::<C>().0);
464 /// #
465 /// # world.insert_resource(A(0));
466 /// # schedule.run(&mut world);
467 /// # assert!(world.resource::<C>().0);
468 /// #
469 /// # world.remove_resource::<A>();
470 /// # world.insert_resource(B(0));
471 /// # world.insert_resource(C(false));
472 /// # schedule.run(&mut world);
473 /// # assert!(world.resource::<C>().0);
474 /// ```
475 fn or_else<M, C: SystemCondition<M, In>>(self, else_run: C) -> OrElse<Self::System, C::System> {
476 let a = IntoSystem::into_system(self);
477 let b = IntoSystem::into_system(else_run);
478 let name = format!("{} || {}", a.name(), b.name());
479 CombinatorSystem::new(a, b, DebugName::owned(name))
480 }
481
482 /// Returns a new run condition that returns `true`
483 /// if either this one or the passed `other` return `true`.
484 ///
485 /// The returned run condition is eagerly evaluated, meaning
486 /// it will always execute both run conditions in order even
487 /// if the first conditions evaluates to `true`.
488 ///
489 /// See also [`or_else`], which short-circuits if `self` returns `true`.
490 ///
491 /// [`or_else`]: SystemCondition::or_else
492 fn or_eager<M, C: SystemCondition<M, In>>(self, other: C) -> OrEager<Self::System, C::System> {
493 let a = IntoSystem::into_system(self);
494 let b = IntoSystem::into_system(other);
495 let name = format!("{} | {}", a.name(), b.name());
496 CombinatorSystem::new(a, b, DebugName::owned(name))
497 }
498
499 /// Returns a new run condition that only returns `true`
500 /// if `self` and `xnor` **both** return `false` or **both** return `true`.
501 ///
502 /// The returned run condition is eagerly evaluated, meaning
503 /// it will always execute both run conditions in order.
504 ///
505 /// # Examples
506 ///
507 /// ```compile_fail
508 /// use bevy::prelude::*;
509 ///
510 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
511 /// pub enum CoffeeMachineState {
512 /// Heating,
513 /// Brewing,
514 /// Inactive,
515 /// }
516 ///
517 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
518 /// pub enum TeaKettleState {
519 /// Heating,
520 /// Steeping,
521 /// Inactive,
522 /// }
523 ///
524 /// # let mut schedule = Schedule::default();
525 /// # let mut world = World::new();
526 /// # fn take_drink_orders() {}
527 /// schedule.add_systems(
528 /// // The take_drink_orders system will only execute if the `in_state(CoffeeMachineState::Inactive)`
529 /// // run condition and `in_state(TeaKettleState::Inactive)` run conditions both evaluate to `false`,
530 /// // or both evaluate to `true`.
531 /// take_drink_orders.run_if(
532 /// in_state(CoffeeMachineState::Inactive).xnor(in_state(TeaKettleState::Inactive))
533 /// ),
534 /// );
535 /// # schedule.run(&mut world);
536 /// ```
537 ///
538 /// Equivalent logic can be achieved by using `not` in concert with `xor`:
539 ///
540 /// ```compile_fail
541 /// schedule.add_systems(
542 /// take_drink_orders.run_if(
543 /// not(in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive)))
544 /// ),
545 /// );
546 /// ```
547 fn xnor<M, C: SystemCondition<M, In>>(self, other: C) -> Xnor<Self::System, C::System> {
548 let a = IntoSystem::into_system(self);
549 let b = IntoSystem::into_system(other);
550 let name = format!("!({} ^ {})", a.name(), b.name());
551 CombinatorSystem::new(a, b, DebugName::owned(name))
552 }
553
554 /// Returns a new run condition that only returns `true`
555 /// if either `self` or `xor` return `true`, but not both.
556 ///
557 /// The returned run condition is eagerly evaluated, meaning
558 /// it will always execute both run conditions in order.
559 ///
560 /// # Examples
561 ///
562 /// ```compile_fail
563 /// use bevy::prelude::*;
564 ///
565 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
566 /// pub enum CoffeeMachineState {
567 /// Heating,
568 /// Brewing,
569 /// Inactive,
570 /// }
571 ///
572 /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
573 /// pub enum TeaKettleState {
574 /// Heating,
575 /// Steeping,
576 /// Inactive,
577 /// }
578 ///
579 /// # let mut schedule = Schedule::default();
580 /// # let mut world = World::new();
581 /// # fn prepare_beverage() {}
582 /// schedule.add_systems(
583 /// // The prepare_beverage system will only execute if either the `in_state(CoffeeMachineState::Inactive)`
584 /// // run condition or `in_state(TeaKettleState::Inactive)` run condition evaluates to `true`,
585 /// // but not both.
586 /// prepare_beverage.run_if(
587 /// in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive))
588 /// ),
589 /// );
590 /// # schedule.run(&mut world);
591 /// ```
592 fn xor<M, C: SystemCondition<M, In>>(self, other: C) -> Xor<Self::System, C::System> {
593 let a = IntoSystem::into_system(self);
594 let b = IntoSystem::into_system(other);
595 let name = format!("({} ^ {})", a.name(), b.name());
596 CombinatorSystem::new(a, b, DebugName::owned(name))
597 }
598}
599
600impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F where
601 F: IntoSystem<In, bool, Marker, System: ReadOnlySystem>
602{
603}
604
605/// A collection of [run conditions](SystemCondition) that may be useful in any bevy app.
606pub mod common_conditions {
607 use super::{NotSystem, SystemCondition};
608 use crate::{
609 change_detection::DetectChanges,
610 lifecycle::RemovedComponents,
611 message::{Message, MessageReader},
612 prelude::{Component, Query, With},
613 query::QueryFilter,
614 resource::Resource,
615 system::{In, IntoSystem, Local, Res, System, SystemInput},
616 };
617 use alloc::format;
618
619 /// A [`SystemCondition`]-satisfying system that returns `true`
620 /// on the first time the condition is run and false every time after.
621 ///
622 /// # Example
623 ///
624 /// ```
625 /// # use bevy_ecs::prelude::*;
626 /// # #[derive(Resource, Default)]
627 /// # struct Counter(u8);
628 /// # let mut schedule = Schedule::default();
629 /// # let mut world = World::new();
630 /// # world.init_resource::<Counter>();
631 /// schedule.add_systems(
632 /// // `run_once` will only return true the first time it's evaluated
633 /// my_system.run_if(run_once),
634 /// );
635 ///
636 /// fn my_system(mut counter: ResMut<Counter>) {
637 /// counter.0 += 1;
638 /// }
639 ///
640 /// // This is the first time the condition will be evaluated so `my_system` will run
641 /// schedule.run(&mut world);
642 /// assert_eq!(world.resource::<Counter>().0, 1);
643 ///
644 /// // This is the seconds time the condition will be evaluated so `my_system` won't run
645 /// schedule.run(&mut world);
646 /// assert_eq!(world.resource::<Counter>().0, 1);
647 /// ```
648 pub fn run_once(mut has_run: Local<bool>) -> bool {
649 if !*has_run {
650 *has_run = true;
651 true
652 } else {
653 false
654 }
655 }
656
657 /// A [`SystemCondition`]-satisfying system that returns `true`
658 /// if the resource exists.
659 ///
660 /// To skip a system with a [`Res`] or [`ResMut`](crate::prelude::ResMut) parameter if the resource does not exist,
661 /// you may instead wrap the parameter in [`If`](crate::prelude::If), like `If<Res<T>>` or `If<ResMut<T>>`.
662 ///
663 /// # Example
664 ///
665 /// ```
666 /// # use bevy_ecs::prelude::*;
667 /// # #[derive(Resource, Default)]
668 /// # struct Counter(u8);
669 /// # let mut schedule = Schedule::default();
670 /// # let mut world = World::new();
671 /// schedule.add_systems(
672 /// // `resource_exists` will only return true if the given resource exists in the world
673 /// my_system.run_if(resource_exists::<Counter>),
674 /// );
675 ///
676 /// fn my_system(mut counter: ResMut<Counter>) {
677 /// counter.0 += 1;
678 /// }
679 ///
680 /// // `Counter` hasn't been added so `my_system` won't run
681 /// schedule.run(&mut world);
682 /// world.init_resource::<Counter>();
683 ///
684 /// // `Counter` has now been added so `my_system` can run
685 /// schedule.run(&mut world);
686 /// assert_eq!(world.resource::<Counter>().0, 1);
687 /// ```
688 pub fn resource_exists<T>(res: Option<Res<T>>) -> bool
689 where
690 T: Resource,
691 {
692 res.is_some()
693 }
694
695 /// Generates a [`SystemCondition`]-satisfying closure that returns `true`
696 /// if the resource is equal to `value`.
697 ///
698 /// # Panics
699 ///
700 /// The condition will panic if the resource does not exist.
701 ///
702 /// # Example
703 ///
704 /// ```
705 /// # use bevy_ecs::prelude::*;
706 /// # #[derive(Resource, Default, PartialEq)]
707 /// # struct Counter(u8);
708 /// # let mut schedule = Schedule::default();
709 /// # let mut world = World::new();
710 /// # world.init_resource::<Counter>();
711 /// schedule.add_systems(
712 /// // `resource_equals` will only return true if the given resource equals the given value
713 /// my_system.run_if(resource_equals(Counter(0))),
714 /// );
715 ///
716 /// fn my_system(mut counter: ResMut<Counter>) {
717 /// counter.0 += 1;
718 /// }
719 ///
720 /// // `Counter` is `0` so `my_system` can run
721 /// schedule.run(&mut world);
722 /// assert_eq!(world.resource::<Counter>().0, 1);
723 ///
724 /// // `Counter` is no longer `0` so `my_system` won't run
725 /// schedule.run(&mut world);
726 /// assert_eq!(world.resource::<Counter>().0, 1);
727 /// ```
728 pub fn resource_equals<T>(value: T) -> impl FnMut(Res<T>) -> bool
729 where
730 T: Resource + PartialEq,
731 {
732 move |res: Res<T>| *res == value
733 }
734
735 /// Generates a [`SystemCondition`]-satisfying closure that returns `true`
736 /// if the resource exists and is equal to `value`.
737 ///
738 /// The condition will return `false` if the resource does not exist.
739 ///
740 /// # Example
741 ///
742 /// ```
743 /// # use bevy_ecs::prelude::*;
744 /// # #[derive(Resource, Default, PartialEq)]
745 /// # struct Counter(u8);
746 /// # let mut schedule = Schedule::default();
747 /// # let mut world = World::new();
748 /// schedule.add_systems(
749 /// // `resource_exists_and_equals` will only return true
750 /// // if the given resource exists and equals the given value
751 /// my_system.run_if(resource_exists_and_equals(Counter(0))),
752 /// );
753 ///
754 /// fn my_system(mut counter: ResMut<Counter>) {
755 /// counter.0 += 1;
756 /// }
757 ///
758 /// // `Counter` hasn't been added so `my_system` can't run
759 /// schedule.run(&mut world);
760 /// world.init_resource::<Counter>();
761 ///
762 /// // `Counter` is `0` so `my_system` can run
763 /// schedule.run(&mut world);
764 /// assert_eq!(world.resource::<Counter>().0, 1);
765 ///
766 /// // `Counter` is no longer `0` so `my_system` won't run
767 /// schedule.run(&mut world);
768 /// assert_eq!(world.resource::<Counter>().0, 1);
769 /// ```
770 pub fn resource_exists_and_equals<T>(value: T) -> impl FnMut(Option<Res<T>>) -> bool
771 where
772 T: Resource + PartialEq,
773 {
774 move |res: Option<Res<T>>| match res {
775 Some(res) => *res == value,
776 None => false,
777 }
778 }
779
780 /// Generates a [`SystemCondition`]-satisfying closure that returns `true`
781 /// if the resource exists and satisfies a closure.
782 ///
783 /// The condition will return `false` if the resource does not exist.
784 ///
785 /// # Example
786 ///
787 /// ```
788 /// # use bevy_ecs::prelude::*;
789 /// # #[derive(Resource, PartialEq)]
790 /// # struct Counter(i16);
791 /// # #[derive(Resource)]
792 /// # struct ShouldRun(String);
793 /// # let mut schedule = Schedule::default();
794 /// # let mut world = World::new();
795 /// schedule.add_systems(
796 /// // `resource_exists_and` will only return true
797 /// // if the given resource exists and satisfies the given condition
798 /// increment.run_if(resource_exists_and(|should_run: &ShouldRun| should_run.0.is_ascii())),
799 /// );
800 ///
801 /// fn increment(mut counter: ResMut<Counter>) {
802 /// counter.0 += 1;
803 /// }
804 ///
805 /// world.insert_resource(Counter(0));
806 ///
807 /// // `ShouldRun` hasn't been added, so `increment` can't run
808 /// schedule.run(&mut world);
809 /// assert_eq!(world.resource::<Counter>().0, 0);
810 /// world.insert_resource(ShouldRun(String::from("bevy")));
811 ///
812 /// // `ShouldRun` exists and satisfies the run conditions, so `increment` can run
813 /// schedule.run(&mut world);
814 /// assert_eq!(world.resource::<Counter>().0, 1);
815 /// world.get_resource_mut::<ShouldRun>().unwrap().0 = String::from("bevy ❤");
816 ///
817 /// // `ShouldRun` exists but has non-ASCII characters and thus
818 /// // does not satisfy the run conditions, so `increment` won't run
819 /// schedule.run(&mut world);
820 /// assert_eq!(world.resource::<Counter>().0, 1);
821 /// ```
822 pub fn resource_exists_and<T>(
823 condition: impl Fn(&T) -> bool,
824 ) -> impl FnMut(Option<Res<T>>) -> bool
825 where
826 T: Resource,
827 {
828 move |res: Option<Res<T>>| match res {
829 Some(res) => condition(&res),
830 None => false,
831 }
832 }
833
834 /// A [`SystemCondition`]-satisfying system that returns `true`
835 /// if the resource of the given type has been added since the condition was last checked.
836 ///
837 /// # Example
838 ///
839 /// ```
840 /// # use bevy_ecs::prelude::*;
841 /// # #[derive(Resource, Default)]
842 /// # struct Counter(u8);
843 /// # let mut schedule = Schedule::default();
844 /// # let mut world = World::new();
845 /// schedule.add_systems(
846 /// // `resource_added` will only return true if the
847 /// // given resource was just added
848 /// my_system.run_if(resource_added::<Counter>),
849 /// );
850 ///
851 /// fn my_system(mut counter: ResMut<Counter>) {
852 /// counter.0 += 1;
853 /// }
854 ///
855 /// world.init_resource::<Counter>();
856 ///
857 /// // `Counter` was just added so `my_system` will run
858 /// schedule.run(&mut world);
859 /// assert_eq!(world.resource::<Counter>().0, 1);
860 ///
861 /// // `Counter` was not just added so `my_system` will not run
862 /// schedule.run(&mut world);
863 /// assert_eq!(world.resource::<Counter>().0, 1);
864 /// ```
865 pub fn resource_added<T>(res: Option<Res<T>>) -> bool
866 where
867 T: Resource,
868 {
869 match res {
870 Some(res) => res.is_added(),
871 None => false,
872 }
873 }
874
875 /// A [`SystemCondition`]-satisfying system that returns `true`
876 /// if the resource of the given type has been added or mutably dereferenced
877 /// since the condition was last checked.
878 ///
879 /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
880 /// Bevy does not compare resources to their previous values.
881 ///
882 /// # Panics
883 ///
884 /// The condition will panic if the resource does not exist.
885 ///
886 /// # Example
887 ///
888 /// ```
889 /// # use bevy_ecs::prelude::*;
890 /// # #[derive(Resource, Default)]
891 /// # struct Counter(u8);
892 /// # let mut schedule = Schedule::default();
893 /// # let mut world = World::new();
894 /// # world.init_resource::<Counter>();
895 /// schedule.add_systems(
896 /// // `resource_changed` will only return true if the
897 /// // given resource was just changed (or added)
898 /// my_system.run_if(
899 /// resource_changed::<Counter>
900 /// // By default detecting changes will also trigger if the resource was
901 /// // just added, this won't work with my example so I will add a second
902 /// // condition to make sure the resource wasn't just added
903 /// .and_then(not(resource_added::<Counter>))
904 /// ),
905 /// );
906 ///
907 /// fn my_system(mut counter: ResMut<Counter>) {
908 /// counter.0 += 1;
909 /// }
910 ///
911 /// // `Counter` hasn't been changed so `my_system` won't run
912 /// schedule.run(&mut world);
913 /// assert_eq!(world.resource::<Counter>().0, 0);
914 ///
915 /// world.resource_mut::<Counter>().0 = 50;
916 ///
917 /// // `Counter` was just changed so `my_system` will run
918 /// schedule.run(&mut world);
919 /// assert_eq!(world.resource::<Counter>().0, 51);
920 /// ```
921 pub fn resource_changed<T>(res: Res<T>) -> bool
922 where
923 T: Resource,
924 {
925 res.is_changed()
926 }
927
928 /// A [`SystemCondition`]-satisfying system that returns `true`
929 /// if the resource of the given type has been added or mutably dereferenced since the condition
930 /// was last checked.
931 ///
932 /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
933 /// Bevy does not compare resources to their previous values.
934 ///
935 /// The condition will return `false` if the resource does not exist.
936 ///
937 /// # Example
938 ///
939 /// ```
940 /// # use bevy_ecs::prelude::*;
941 /// # #[derive(Resource, Default)]
942 /// # struct Counter(u8);
943 /// # let mut schedule = Schedule::default();
944 /// # let mut world = World::new();
945 /// schedule.add_systems(
946 /// // `resource_exists_and_changed` will only return true if the
947 /// // given resource exists and was just changed (or added)
948 /// my_system.run_if(
949 /// resource_exists_and_changed::<Counter>
950 /// // By default detecting changes will also trigger if the resource was
951 /// // just added, this won't work with my example so I will add a second
952 /// // condition to make sure the resource wasn't just added
953 /// .and_then(not(resource_added::<Counter>))
954 /// ),
955 /// );
956 ///
957 /// fn my_system(mut counter: ResMut<Counter>) {
958 /// counter.0 += 1;
959 /// }
960 ///
961 /// // `Counter` doesn't exist so `my_system` won't run
962 /// schedule.run(&mut world);
963 /// world.init_resource::<Counter>();
964 ///
965 /// // `Counter` hasn't been changed so `my_system` won't run
966 /// schedule.run(&mut world);
967 /// assert_eq!(world.resource::<Counter>().0, 0);
968 ///
969 /// world.resource_mut::<Counter>().0 = 50;
970 ///
971 /// // `Counter` was just changed so `my_system` will run
972 /// schedule.run(&mut world);
973 /// assert_eq!(world.resource::<Counter>().0, 51);
974 /// ```
975 pub fn resource_exists_and_changed<T>(res: Option<Res<T>>) -> bool
976 where
977 T: Resource,
978 {
979 match res {
980 Some(res) => res.is_changed(),
981 None => false,
982 }
983 }
984
985 /// A [`SystemCondition`]-satisfying system that returns `true`
986 /// if the resource of the given type has been added, removed or mutably dereferenced since the condition
987 /// was last checked.
988 ///
989 /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
990 /// Bevy does not compare resources to their previous values.
991 ///
992 /// The condition will return `false` if the resource does not exist.
993 ///
994 /// # Example
995 ///
996 /// ```
997 /// # use bevy_ecs::prelude::*;
998 /// # #[derive(Resource, Default)]
999 /// # struct Counter(u8);
1000 /// # let mut schedule = Schedule::default();
1001 /// # let mut world = World::new();
1002 /// # world.init_resource::<Counter>();
1003 /// schedule.add_systems(
1004 /// // `resource_changed_or_removed` will only return true if the
1005 /// // given resource was just changed or removed (or added)
1006 /// my_system.run_if(
1007 /// resource_changed_or_removed::<Counter>
1008 /// // By default detecting changes will also trigger if the resource was
1009 /// // just added, this won't work with my example so I will add a second
1010 /// // condition to make sure the resource wasn't just added
1011 /// .and_then(not(resource_added::<Counter>))
1012 /// ),
1013 /// );
1014 ///
1015 /// #[derive(Resource, Default)]
1016 /// struct MyResource;
1017 ///
1018 /// // If `Counter` exists, increment it, otherwise insert `MyResource`
1019 /// fn my_system(mut commands: Commands, mut counter: Option<ResMut<Counter>>) {
1020 /// if let Some(mut counter) = counter {
1021 /// counter.0 += 1;
1022 /// } else {
1023 /// commands.init_resource::<MyResource>();
1024 /// }
1025 /// }
1026 ///
1027 /// // `Counter` hasn't been changed so `my_system` won't run
1028 /// schedule.run(&mut world);
1029 /// assert_eq!(world.resource::<Counter>().0, 0);
1030 ///
1031 /// world.resource_mut::<Counter>().0 = 50;
1032 ///
1033 /// // `Counter` was just changed so `my_system` will run
1034 /// schedule.run(&mut world);
1035 /// assert_eq!(world.resource::<Counter>().0, 51);
1036 ///
1037 /// world.remove_resource::<Counter>();
1038 ///
1039 /// // `Counter` was just removed so `my_system` will run
1040 /// schedule.run(&mut world);
1041 /// assert_eq!(world.contains_resource::<MyResource>(), true);
1042 /// ```
1043 pub fn resource_changed_or_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
1044 where
1045 T: Resource,
1046 {
1047 if let Some(value) = res {
1048 *existed = true;
1049 value.is_changed()
1050 } else if *existed {
1051 *existed = false;
1052 true
1053 } else {
1054 false
1055 }
1056 }
1057
1058 /// A [`SystemCondition`]-satisfying system that returns `true`
1059 /// if the resource of the given type has been removed since the condition was last checked.
1060 ///
1061 /// # Example
1062 ///
1063 /// ```
1064 /// # use bevy_ecs::prelude::*;
1065 /// # #[derive(Resource, Default)]
1066 /// # struct Counter(u8);
1067 /// # let mut schedule = Schedule::default();
1068 /// # let mut world = World::new();
1069 /// # world.init_resource::<Counter>();
1070 /// schedule.add_systems(
1071 /// // `resource_removed` will only return true if the
1072 /// // given resource was just removed
1073 /// my_system.run_if(resource_removed::<MyResource>),
1074 /// );
1075 ///
1076 /// #[derive(Resource, Default)]
1077 /// struct MyResource;
1078 ///
1079 /// fn my_system(mut counter: ResMut<Counter>) {
1080 /// counter.0 += 1;
1081 /// }
1082 ///
1083 /// world.init_resource::<MyResource>();
1084 ///
1085 /// // `MyResource` hasn't just been removed so `my_system` won't run
1086 /// schedule.run(&mut world);
1087 /// assert_eq!(world.resource::<Counter>().0, 0);
1088 ///
1089 /// world.remove_resource::<MyResource>();
1090 ///
1091 /// // `MyResource` was just removed so `my_system` will run
1092 /// schedule.run(&mut world);
1093 /// assert_eq!(world.resource::<Counter>().0, 1);
1094 /// ```
1095 pub fn resource_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
1096 where
1097 T: Resource,
1098 {
1099 if res.is_some() {
1100 *existed = true;
1101 false
1102 } else if *existed {
1103 *existed = false;
1104 true
1105 } else {
1106 false
1107 }
1108 }
1109
1110 /// A [`SystemCondition`]-satisfying system that returns `true`
1111 /// if there are any new messages of the given type since it was last called.
1112 ///
1113 /// To skip a system based on messages that it reads, use [`PopulatedMessageReader`](crate::prelude::PopulatedMessageReader) instead.
1114 ///
1115 /// # Example
1116 ///
1117 /// ```
1118 /// # use bevy_ecs::prelude::*;
1119 /// # #[derive(Resource, Default)]
1120 /// # struct Counter(u8);
1121 /// # let mut schedule = Schedule::default();
1122 /// # let mut world = World::new();
1123 /// # world.init_resource::<Counter>();
1124 /// # world.init_resource::<Messages<MyMessage>>();
1125 /// # schedule.add_systems(bevy_ecs::message::message_update_system.before(my_system));
1126 ///
1127 /// schedule.add_systems(
1128 /// my_system.run_if(on_message::<MyMessage>),
1129 /// );
1130 ///
1131 /// #[derive(Message)]
1132 /// struct MyMessage;
1133 ///
1134 /// fn my_system(mut counter: ResMut<Counter>) {
1135 /// counter.0 += 1;
1136 /// }
1137 ///
1138 /// // No new `MyMessage` messages have been pushed so `my_system` won't run
1139 /// schedule.run(&mut world);
1140 /// assert_eq!(world.resource::<Counter>().0, 0);
1141 ///
1142 /// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);
1143 ///
1144 /// // A `MyMessage` message has been pushed so `my_system` will run
1145 /// schedule.run(&mut world);
1146 /// assert_eq!(world.resource::<Counter>().0, 1);
1147 /// ```
1148 pub fn on_message<M: Message>(mut reader: MessageReader<M>) -> bool {
1149 // The messages need to be consumed, so that there are no false positives on subsequent
1150 // calls of the run condition. Simply checking `is_empty` would not be enough.
1151 // PERF: note that `count` is efficient (not actually looping/iterating),
1152 // due to Bevy having a specialized implementation for messages.
1153 reader.read().count() > 0
1154 }
1155
1156 /// A [`SystemCondition`]-satisfying system that returns `true`
1157 /// if there are any entities with the given component type.
1158 ///
1159 /// This is equivalent to [`any_match_filter::<With<T>>()`]
1160 ///
1161 /// To skip a system with a [`Query`] parameter if the query is empty,
1162 /// you may instead use [`Populated`](crate::prelude::Populated), if the query may match multiple entities,
1163 /// or [`Single`](crate::prelude::Single), if it will only match one.
1164 ///
1165 /// # Example
1166 ///
1167 /// ```
1168 /// # use bevy_ecs::prelude::*;
1169 /// # #[derive(Resource, Default)]
1170 /// # struct Counter(u8);
1171 /// # let mut schedule = Schedule::default();
1172 /// # let mut world = World::new();
1173 /// # world.init_resource::<Counter>();
1174 /// schedule.add_systems(
1175 /// my_system.run_if(any_with_component::<MyComponent>),
1176 /// );
1177 ///
1178 /// #[derive(Component)]
1179 /// struct MyComponent;
1180 ///
1181 /// fn my_system(mut counter: ResMut<Counter>) {
1182 /// counter.0 += 1;
1183 /// }
1184 ///
1185 /// // No entities exist yet with a `MyComponent` component so `my_system` won't run
1186 /// schedule.run(&mut world);
1187 /// assert_eq!(world.resource::<Counter>().0, 0);
1188 ///
1189 /// world.spawn(MyComponent);
1190 ///
1191 /// // An entities with `MyComponent` now exists so `my_system` will run
1192 /// schedule.run(&mut world);
1193 /// assert_eq!(world.resource::<Counter>().0, 1);
1194 /// ```
1195 pub fn any_with_component<T: Component>(query: Query<(), With<T>>) -> bool {
1196 !query.is_empty()
1197 }
1198
1199 /// A [`SystemCondition`]-satisfying system that returns `true`
1200 /// if there are any entity with a component of the given type removed.
1201 pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {
1202 // `RemovedComponents` based on events and therefore events need to be consumed,
1203 // so that there are no false positives on subsequent calls of the run condition.
1204 // Simply checking `is_empty` would not be enough.
1205 // PERF: note that `count` is efficient (not actually looping/iterating),
1206 // due to Bevy having a specialized implementation for events.
1207 removals.read().count() > 0
1208 }
1209
1210 /// A [`SystemCondition`]-satisfying system that returns `true`
1211 /// if there are any entities that match the given [`QueryFilter`].
1212 ///
1213 /// For a simple `With<T>` filter, this is equivalent to [`any_with_component::<T>()`].
1214 ///
1215 /// To skip a system with a [`Query`] parameter if the query is empty,
1216 /// you may instead use [`Populated`](crate::prelude::Populated), if the query may match multiple entities,
1217 /// or [`Single`](crate::prelude::Single), if it will only match one.
1218 pub fn any_match_filter<F: QueryFilter>(query: Query<(), F>) -> bool {
1219 !query.is_empty()
1220 }
1221
1222 /// Generates a [`SystemCondition`] that inverses the result of passed one.
1223 ///
1224 /// # Example
1225 ///
1226 /// ```
1227 /// # use bevy_ecs::prelude::*;
1228 /// # #[derive(Resource, Default)]
1229 /// # struct Counter(u8);
1230 /// # let mut schedule = Schedule::default();
1231 /// # let mut world = World::new();
1232 /// # world.init_resource::<Counter>();
1233 /// schedule.add_systems(
1234 /// // `not` will inverse any condition you pass in.
1235 /// // Since the condition we choose always returns true
1236 /// // this system will never run
1237 /// my_system.run_if(not(always)),
1238 /// );
1239 ///
1240 /// fn my_system(mut counter: ResMut<Counter>) {
1241 /// counter.0 += 1;
1242 /// }
1243 ///
1244 /// fn always() -> bool {
1245 /// true
1246 /// }
1247 ///
1248 /// schedule.run(&mut world);
1249 /// assert_eq!(world.resource::<Counter>().0, 0);
1250 /// ```
1251 pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>
1252 where
1253 TOut: core::ops::Not,
1254 T: IntoSystem<(), TOut, Marker>,
1255 {
1256 let condition = IntoSystem::into_system(condition);
1257 let name = format!("!{}", condition.name());
1258 NotSystem::new(super::NotMarker, condition, name.into())
1259 }
1260
1261 /// Generates a [`SystemCondition`] that returns true when the passed one changes.
1262 ///
1263 /// The first time this is called, the passed condition is assumed to have been previously false.
1264 ///
1265 /// # Example
1266 ///
1267 /// ```
1268 /// # use bevy_ecs::prelude::*;
1269 /// # #[derive(Resource, Default)]
1270 /// # struct Counter(u8);
1271 /// # let mut schedule = Schedule::default();
1272 /// # let mut world = World::new();
1273 /// # world.init_resource::<Counter>();
1274 /// schedule.add_systems(
1275 /// my_system.run_if(condition_changed(resource_exists::<MyResource>)),
1276 /// );
1277 ///
1278 /// #[derive(Resource)]
1279 /// struct MyResource;
1280 ///
1281 /// fn my_system(mut counter: ResMut<Counter>) {
1282 /// counter.0 += 1;
1283 /// }
1284 ///
1285 /// // `MyResource` is initially there, the inner condition is true, the system runs once
1286 /// world.insert_resource(MyResource);
1287 /// schedule.run(&mut world);
1288 /// assert_eq!(world.resource::<Counter>().0, 1);
1289 /// schedule.run(&mut world);
1290 /// assert_eq!(world.resource::<Counter>().0, 1);
1291 ///
1292 /// // We remove `MyResource`, the inner condition is now false, the system runs one more time.
1293 /// world.remove_resource::<MyResource>();
1294 /// schedule.run(&mut world);
1295 /// assert_eq!(world.resource::<Counter>().0, 2);
1296 /// schedule.run(&mut world);
1297 /// assert_eq!(world.resource::<Counter>().0, 2);
1298 /// ```
1299 pub fn condition_changed<Marker, CIn, C>(condition: C) -> impl SystemCondition<(), CIn>
1300 where
1301 CIn: SystemInput,
1302 C: SystemCondition<Marker, CIn>,
1303 {
1304 IntoSystem::into_system(condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| {
1305 let changed = *prev != new;
1306 *prev = new;
1307 changed
1308 }))
1309 }
1310
1311 /// Generates a [`SystemCondition`] that returns true when the result of
1312 /// the passed one went from false to true since the last time this was called.
1313 ///
1314 /// The first time this is called, the passed condition is assumed to have been previously false.
1315 ///
1316 /// # Example
1317 ///
1318 /// ```
1319 /// # use bevy_ecs::prelude::*;
1320 /// # #[derive(Resource, Default)]
1321 /// # struct Counter(u8);
1322 /// # let mut schedule = Schedule::default();
1323 /// # let mut world = World::new();
1324 /// # world.init_resource::<Counter>();
1325 /// schedule.add_systems(
1326 /// my_system.run_if(condition_changed_to(true, resource_exists::<MyResource>)),
1327 /// );
1328 ///
1329 /// #[derive(Resource)]
1330 /// struct MyResource;
1331 ///
1332 /// fn my_system(mut counter: ResMut<Counter>) {
1333 /// counter.0 += 1;
1334 /// }
1335 ///
1336 /// // `MyResource` is initially there, the inner condition is true, the system runs once
1337 /// world.insert_resource(MyResource);
1338 /// schedule.run(&mut world);
1339 /// assert_eq!(world.resource::<Counter>().0, 1);
1340 /// schedule.run(&mut world);
1341 /// assert_eq!(world.resource::<Counter>().0, 1);
1342 ///
1343 /// // We remove `MyResource`, the inner condition is now false, the system doesn't run.
1344 /// world.remove_resource::<MyResource>();
1345 /// schedule.run(&mut world);
1346 /// assert_eq!(world.resource::<Counter>().0, 1);
1347 ///
1348 /// // We reinsert `MyResource` again, so the system will run one more time
1349 /// world.insert_resource(MyResource);
1350 /// schedule.run(&mut world);
1351 /// assert_eq!(world.resource::<Counter>().0, 2);
1352 /// schedule.run(&mut world);
1353 /// assert_eq!(world.resource::<Counter>().0, 2);
1354 /// ```
1355 pub fn condition_changed_to<Marker, CIn, C>(
1356 to: bool,
1357 condition: C,
1358 ) -> impl SystemCondition<(), CIn>
1359 where
1360 CIn: SystemInput,
1361 C: SystemCondition<Marker, CIn>,
1362 {
1363 IntoSystem::into_system(condition.pipe(
1364 move |In(new): In<bool>, mut prev: Local<bool>| -> bool {
1365 let now_true = *prev != new && new == to;
1366 *prev = new;
1367 now_true
1368 },
1369 ))
1370 }
1371}
1372
1373/// Invokes [`Not`] with the output of another system.
1374///
1375/// See [`common_conditions::not`] for examples.
1376pub type NotSystem<S> = AdapterSystem<NotMarker, S>;
1377
1378/// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator.
1379#[doc(hidden)]
1380#[derive(Clone, Copy)]
1381pub struct NotMarker;
1382
1383impl<S: System<Out: Not>> Adapt<S> for NotMarker {
1384 type In = S::In;
1385 type Out = <S::Out as Not>::Output;
1386
1387 fn adapt(
1388 &mut self,
1389 input: <Self::In as SystemInput>::Inner<'_>,
1390 run_system: impl FnOnce(SystemIn<'_, S>) -> Result<S::Out, RunSystemError>,
1391 ) -> Result<Self::Out, RunSystemError> {
1392 run_system(input).map(Not::not)
1393 }
1394}
1395
1396/// Combines the outputs of two systems using the `&&` operator (short-circuiting).
1397pub type AndThen<A, B> = CombinatorSystem<AndThenMarker, A, B>;
1398
1399/// Combines the outputs of two systems using the `&` operator (eagerly evaluated).
1400pub type AndEager<A, B> = CombinatorSystem<AndEagerMarker, A, B>;
1401
1402/// Combines and inverts the outputs of two systems using the `&&` and `!` operators (short-circuiting).
1403pub type NandThen<A, B> = CombinatorSystem<NandThenMarker, A, B>;
1404
1405/// Combines and inverts the outputs of two systems using the `&` and `!` operators (eagerly evaluated).
1406pub type NandEager<A, B> = CombinatorSystem<NandEagerMarker, A, B>;
1407
1408/// Combines and inverts the outputs of two systems using the `||` and `!` operators (short-circuiting).
1409pub type NorElse<A, B> = CombinatorSystem<NorElseMarker, A, B>;
1410
1411/// Combines and inverts the outputs of two systems using the `|` and `!` operators (eagerly evaluated).
1412pub type NorEager<A, B> = CombinatorSystem<NorEagerMarker, A, B>;
1413
1414/// Combines the outputs of two systems using the `||` operator (short-circuiting).
1415pub type OrElse<A, B> = CombinatorSystem<OrElseMarker, A, B>;
1416
1417/// Combines the outputs of two systems using the `|` operator (short-circuiting).
1418pub type OrEager<A, B> = CombinatorSystem<OrEagerMarker, A, B>;
1419
1420/// Combines and inverts the outputs of two systems using the `^` and `!` operators (eagerly evaluated).
1421pub type Xnor<A, B> = CombinatorSystem<XnorMarker, A, B>;
1422
1423/// Combines the outputs of two systems using the `^` operator (eagerly evaluated).
1424pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;
1425
1426#[doc(hidden)]
1427pub struct AndThenMarker;
1428
1429impl<In, A, B> Combine<A, B> for AndThenMarker
1430where
1431 for<'a> In: SystemInput<Inner<'a>: Copy>,
1432 A: System<In = In, Out = bool>,
1433 B: System<In = In, Out = bool>,
1434{
1435 type In = In;
1436 type Out = bool;
1437
1438 fn combine<T>(
1439 input: <Self::In as SystemInput>::Inner<'_>,
1440 data: &mut T,
1441 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1442 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1443 ) -> Result<Self::Out, RunSystemError> {
1444 Ok(a(input, data).unwrap_or(false) && b(input, data).unwrap_or(false))
1445 }
1446}
1447
1448#[doc(hidden)]
1449pub struct AndEagerMarker;
1450
1451impl<In, A, B> Combine<A, B> for AndEagerMarker
1452where
1453 for<'a> In: SystemInput<Inner<'a>: Copy>,
1454 A: System<In = In, Out = bool>,
1455 B: System<In = In, Out = bool>,
1456{
1457 type In = In;
1458 type Out = bool;
1459
1460 fn combine<T>(
1461 input: <Self::In as SystemInput>::Inner<'_>,
1462 data: &mut T,
1463 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1464 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1465 ) -> Result<Self::Out, RunSystemError> {
1466 Ok(a(input, data).unwrap_or(false) & b(input, data).unwrap_or(false))
1467 }
1468}
1469
1470#[doc(hidden)]
1471pub struct NandThenMarker;
1472
1473impl<In, A, B> Combine<A, B> for NandThenMarker
1474where
1475 for<'a> In: SystemInput<Inner<'a>: Copy>,
1476 A: System<In = In, Out = bool>,
1477 B: System<In = In, Out = bool>,
1478{
1479 type In = In;
1480 type Out = bool;
1481
1482 fn combine<T>(
1483 input: <Self::In as SystemInput>::Inner<'_>,
1484 data: &mut T,
1485 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1486 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1487 ) -> Result<Self::Out, RunSystemError> {
1488 Ok(!(a(input, data).unwrap_or(false) && b(input, data).unwrap_or(false)))
1489 }
1490}
1491
1492#[doc(hidden)]
1493pub struct NandEagerMarker;
1494
1495impl<In, A, B> Combine<A, B> for NandEagerMarker
1496where
1497 for<'a> In: SystemInput<Inner<'a>: Copy>,
1498 A: System<In = In, Out = bool>,
1499 B: System<In = In, Out = bool>,
1500{
1501 type In = In;
1502 type Out = bool;
1503
1504 fn combine<T>(
1505 input: <Self::In as SystemInput>::Inner<'_>,
1506 data: &mut T,
1507 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1508 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1509 ) -> Result<Self::Out, RunSystemError> {
1510 Ok(!(a(input, data).unwrap_or(false) & b(input, data).unwrap_or(false)))
1511 }
1512}
1513
1514#[doc(hidden)]
1515pub struct NorElseMarker;
1516
1517impl<In, A, B> Combine<A, B> for NorElseMarker
1518where
1519 for<'a> In: SystemInput<Inner<'a>: Copy>,
1520 A: System<In = In, Out = bool>,
1521 B: System<In = In, Out = bool>,
1522{
1523 type In = In;
1524 type Out = bool;
1525
1526 fn combine<T>(
1527 input: <Self::In as SystemInput>::Inner<'_>,
1528 data: &mut T,
1529 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1530 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1531 ) -> Result<Self::Out, RunSystemError> {
1532 Ok(!(a(input, data).unwrap_or(false) || b(input, data).unwrap_or(false)))
1533 }
1534}
1535
1536#[doc(hidden)]
1537pub struct NorEagerMarker;
1538
1539impl<In, A, B> Combine<A, B> for NorEagerMarker
1540where
1541 for<'a> In: SystemInput<Inner<'a>: Copy>,
1542 A: System<In = In, Out = bool>,
1543 B: System<In = In, Out = bool>,
1544{
1545 type In = In;
1546 type Out = bool;
1547
1548 fn combine<T>(
1549 input: <Self::In as SystemInput>::Inner<'_>,
1550 data: &mut T,
1551 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1552 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1553 ) -> Result<Self::Out, RunSystemError> {
1554 Ok(!(a(input, data).unwrap_or(false) | b(input, data).unwrap_or(false)))
1555 }
1556}
1557
1558#[doc(hidden)]
1559pub struct OrElseMarker;
1560
1561impl<In, A, B> Combine<A, B> for OrElseMarker
1562where
1563 for<'a> In: SystemInput<Inner<'a>: Copy>,
1564 A: System<In = In, Out = bool>,
1565 B: System<In = In, Out = bool>,
1566{
1567 type In = In;
1568 type Out = bool;
1569
1570 fn combine<T>(
1571 input: <Self::In as SystemInput>::Inner<'_>,
1572 data: &mut T,
1573 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1574 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1575 ) -> Result<Self::Out, RunSystemError> {
1576 Ok(a(input, data).unwrap_or(false) || b(input, data).unwrap_or(false))
1577 }
1578}
1579
1580#[doc(hidden)]
1581pub struct OrEagerMarker;
1582
1583impl<In, A, B> Combine<A, B> for OrEagerMarker
1584where
1585 for<'a> In: SystemInput<Inner<'a>: Copy>,
1586 A: System<In = In, Out = bool>,
1587 B: System<In = In, Out = bool>,
1588{
1589 type In = In;
1590 type Out = bool;
1591
1592 fn combine<T>(
1593 input: <Self::In as SystemInput>::Inner<'_>,
1594 data: &mut T,
1595 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1596 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1597 ) -> Result<Self::Out, RunSystemError> {
1598 Ok(a(input, data).unwrap_or(false) | b(input, data).unwrap_or(false))
1599 }
1600}
1601
1602#[doc(hidden)]
1603pub struct XnorMarker;
1604
1605impl<In, A, B> Combine<A, B> for XnorMarker
1606where
1607 for<'a> In: SystemInput<Inner<'a>: Copy>,
1608 A: System<In = In, Out = bool>,
1609 B: System<In = In, Out = bool>,
1610{
1611 type In = In;
1612 type Out = bool;
1613
1614 fn combine<T>(
1615 input: <Self::In as SystemInput>::Inner<'_>,
1616 data: &mut T,
1617 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1618 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1619 ) -> Result<Self::Out, RunSystemError> {
1620 Ok(!(a(input, data).unwrap_or(false) ^ b(input, data).unwrap_or(false)))
1621 }
1622}
1623
1624#[doc(hidden)]
1625pub struct XorMarker;
1626
1627impl<In, A, B> Combine<A, B> for XorMarker
1628where
1629 for<'a> In: SystemInput<Inner<'a>: Copy>,
1630 A: System<In = In, Out = bool>,
1631 B: System<In = In, Out = bool>,
1632{
1633 type In = In;
1634 type Out = bool;
1635
1636 fn combine<T>(
1637 input: <Self::In as SystemInput>::Inner<'_>,
1638 data: &mut T,
1639 a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1640 b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1641 ) -> Result<Self::Out, RunSystemError> {
1642 Ok(a(input, data).unwrap_or(false) ^ b(input, data).unwrap_or(false))
1643 }
1644}
1645
1646#[cfg(test)]
1647mod tests {
1648 use super::{common_conditions::*, SystemCondition};
1649 use crate::error::{BevyError, ErrorContext, FallbackErrorHandler};
1650 use crate::{
1651 change_detection::{Res, ResMut},
1652 component::Component,
1653 message::Message,
1654 query::With,
1655 schedule::{IntoScheduleConfigs, Schedule},
1656 system::{IntoSystem, Local, System},
1657 world::World,
1658 };
1659 use bevy_ecs_macros::{Resource, SystemSet};
1660
1661 #[derive(Resource, Default)]
1662 struct Counter(usize);
1663
1664 fn increment_counter(mut counter: ResMut<Counter>) {
1665 counter.0 += 1;
1666 }
1667
1668 fn double_counter(mut counter: ResMut<Counter>) {
1669 counter.0 *= 2;
1670 }
1671
1672 fn every_other_time(mut has_ran: Local<bool>) -> bool {
1673 *has_ran = !*has_ran;
1674 *has_ran
1675 }
1676
1677 #[test]
1678 fn run_condition() {
1679 let mut world = World::new();
1680 world.init_resource::<Counter>();
1681 let mut schedule = Schedule::default();
1682
1683 // Run every other cycle
1684 schedule.add_systems(increment_counter.run_if(every_other_time));
1685
1686 schedule.run(&mut world);
1687 schedule.run(&mut world);
1688 assert_eq!(world.resource::<Counter>().0, 1);
1689 schedule.run(&mut world);
1690 schedule.run(&mut world);
1691 assert_eq!(world.resource::<Counter>().0, 2);
1692
1693 // Run every other cycle opposite to the last one
1694 schedule.add_systems(increment_counter.run_if(not(every_other_time)));
1695
1696 schedule.run(&mut world);
1697 schedule.run(&mut world);
1698 assert_eq!(world.resource::<Counter>().0, 4);
1699 schedule.run(&mut world);
1700 schedule.run(&mut world);
1701 assert_eq!(world.resource::<Counter>().0, 6);
1702 }
1703
1704 #[test]
1705 fn combinators_with_maybe_failing_condition() {
1706 #![allow(
1707 clippy::nonminimal_bool,
1708 clippy::overly_complex_bool_expr,
1709 reason = "Trailing `|| false` and `&& true` are used in this test to visually remain consistent with the combinators"
1710 )]
1711
1712 use crate::system::RunSystemOnce;
1713 use alloc::sync::Arc;
1714 use std::sync::Mutex;
1715
1716 // Things that should be tested:
1717 // - the final result of the combinator is correct
1718 // - the systems that are expected to run do run
1719 // - the systems that are expected to not run do not run
1720
1721 #[derive(Component)]
1722 struct Vacant;
1723
1724 // SystemConditions don't have mutable access to the world, so we use a
1725 // `Res<AtomicCounter>` to count invocations.
1726 #[derive(Resource, Default)]
1727 struct Counter(Arc<Mutex<usize>>);
1728
1729 // The following constants are used to represent a system having run.
1730 // both are prime so that when multiplied they give a unique value for any TRUE^n*FALSE^m
1731 const FALSE: usize = 2;
1732 const TRUE: usize = 3;
1733
1734 // this is a system, but has the same side effect as `test_true`
1735 fn is_true_inc(counter: Res<Counter>) -> bool {
1736 test_true(&counter)
1737 }
1738
1739 // this is a system, but has the same side effect as `test_false`
1740 fn is_false_inc(counter: Res<Counter>) -> bool {
1741 test_false(&counter)
1742 }
1743
1744 // This condition will always yield `false`, because `Vacant` is never present.
1745 fn vacant(_: crate::system::Single<&Vacant>) -> bool {
1746 true
1747 }
1748
1749 fn test_true(counter: &Counter) -> bool {
1750 *counter.0.lock().unwrap() *= TRUE;
1751 true
1752 }
1753
1754 fn test_false(counter: &Counter) -> bool {
1755 *counter.0.lock().unwrap() *= FALSE;
1756 false
1757 }
1758
1759 // Helper function that runs a logic call and returns the result, as
1760 // well as the composite number of the calls.
1761 fn logic_call_result(f: impl FnOnce(&Counter) -> bool) -> (usize, bool) {
1762 let counter = Counter(Arc::new(Mutex::new(1)));
1763 let result = f(&counter);
1764 (*counter.0.lock().unwrap(), result)
1765 }
1766
1767 // `test_true` and `test_false` can't fail like the systems can, and so
1768 // we use them to model the short circuiting behavior of rust's logical
1769 // operators. The goal is to end up with a composite number that
1770 // describes rust's behavior and compare that to the result of the
1771 // combinators.
1772
1773 // we expect `true() || false()` to yield `true`, and short circuit
1774 // after `true()`
1775 assert_eq!(
1776 logic_call_result(|c| test_true(c) || test_false(c)),
1777 (TRUE.pow(1) * FALSE.pow(0), true)
1778 );
1779
1780 let mut world = World::new();
1781 world.init_resource::<Counter>();
1782
1783 // ensure there are no `Vacant` entities
1784 assert!(world.query::<&Vacant>().iter(&world).next().is_none());
1785 assert!(matches!(
1786 world.run_system_once((|| true).or_else(vacant)),
1787 Ok(true)
1788 ));
1789
1790 // This system should fail
1791 assert!(RunSystemOnce::run_system_once(&mut world, vacant).is_err());
1792
1793 #[track_caller]
1794 fn assert_system<Marker>(
1795 world: &mut World,
1796 system: impl IntoSystem<(), bool, Marker>,
1797 equivalent_to: impl FnOnce(&Counter) -> bool,
1798 ) {
1799 use crate::system::System;
1800
1801 *world.resource::<Counter>().0.lock().unwrap() = 1;
1802
1803 let system = IntoSystem::into_system(system);
1804 let name = system.name();
1805
1806 let out = RunSystemOnce::run_system_once(&mut *world, system).unwrap_or(false);
1807
1808 let (expected_counter, expected) = logic_call_result(equivalent_to);
1809 let caller = core::panic::Location::caller();
1810 let counter = *world.resource::<Counter>().0.lock().unwrap();
1811
1812 assert_eq!(
1813 out,
1814 expected,
1815 "At {}:{} System `{name}` yielded unexpected value `{out}`, expected `{expected}`",
1816 caller.file(),
1817 caller.line(),
1818 );
1819
1820 assert_eq!(
1821 counter, expected_counter,
1822 "At {}:{} System `{name}` did not increment counter as expected: expected `{expected_counter}`, got `{counter}`",
1823 caller.file(),
1824 caller.line(),
1825 );
1826 }
1827
1828 assert_system(&mut world, is_true_inc.or_else(vacant), |c| {
1829 test_true(c) || false
1830 });
1831 assert_system(&mut world, is_true_inc.nor_else(vacant), |c| {
1832 !(test_true(c) || false)
1833 });
1834 assert_system(&mut world, is_true_inc.xor(vacant), |c| {
1835 test_true(c) ^ false
1836 });
1837 assert_system(&mut world, is_true_inc.xnor(vacant), |c| {
1838 !(test_true(c) ^ false)
1839 });
1840 assert_system(&mut world, is_true_inc.and_then(vacant), |c| {
1841 test_true(c) && false
1842 });
1843 assert_system(&mut world, is_true_inc.nand_then(vacant), |c| {
1844 !(test_true(c) && false)
1845 });
1846
1847 // even if `vacant` fails as the first condition, where applicable (or,
1848 // xor), `is_true_inc` should still be called. `and` and `nand` short
1849 // circuit on an initial `false`.
1850 assert_system(&mut world, vacant.or_else(is_true_inc), |c| {
1851 false || test_true(c)
1852 });
1853 assert_system(&mut world, vacant.nor_else(is_true_inc), |c| {
1854 !(false || test_true(c))
1855 });
1856 assert_system(&mut world, vacant.xor(is_true_inc), |c| {
1857 false ^ test_true(c)
1858 });
1859 assert_system(&mut world, vacant.xnor(is_true_inc), |c| {
1860 !(false ^ test_true(c))
1861 });
1862 assert_system(&mut world, vacant.and_then(is_true_inc), |c| {
1863 false && test_true(c)
1864 });
1865 assert_system(&mut world, vacant.nand_then(is_true_inc), |c| {
1866 !(false && test_true(c))
1867 });
1868
1869 // the same logic ought to be the case with a condition that runs, but yields `false`:
1870 assert_system(&mut world, is_true_inc.or_else(is_false_inc), |c| {
1871 test_true(c) || test_false(c)
1872 });
1873 assert_system(&mut world, is_true_inc.nor_else(is_false_inc), |c| {
1874 !(test_true(c) || test_false(c))
1875 });
1876 assert_system(&mut world, is_true_inc.xor(is_false_inc), |c| {
1877 test_true(c) ^ test_false(c)
1878 });
1879 assert_system(&mut world, is_true_inc.xnor(is_false_inc), |c| {
1880 !(test_true(c) ^ test_false(c))
1881 });
1882 assert_system(&mut world, is_true_inc.and_then(is_false_inc), |c| {
1883 test_true(c) && test_false(c)
1884 });
1885 assert_system(&mut world, is_true_inc.nand_then(is_false_inc), |c| {
1886 !(test_true(c) && test_false(c))
1887 });
1888
1889 // and where one condition yields `false` and the other fails:
1890 assert_system(&mut world, is_false_inc.or_else(vacant), |c| {
1891 test_false(c) || false
1892 });
1893 assert_system(&mut world, is_false_inc.nor_else(vacant), |c| {
1894 !(test_false(c) || false)
1895 });
1896 assert_system(&mut world, is_false_inc.xor(vacant), |c| {
1897 test_false(c) ^ false
1898 });
1899 assert_system(&mut world, is_false_inc.xnor(vacant), |c| {
1900 !(test_false(c) ^ false)
1901 });
1902 assert_system(&mut world, is_false_inc.and_then(vacant), |c| {
1903 test_false(c) && false
1904 });
1905 assert_system(&mut world, is_false_inc.nand_then(vacant), |c| {
1906 !(test_false(c) && false)
1907 });
1908
1909 // and where both conditions yield `true`:
1910 assert_system(&mut world, is_true_inc.or_else(is_true_inc), |c| {
1911 test_true(c) || test_true(c)
1912 });
1913 assert_system(&mut world, is_true_inc.nor_else(is_true_inc), |c| {
1914 !(test_true(c) || test_true(c))
1915 });
1916 assert_system(&mut world, is_true_inc.xor(is_true_inc), |c| {
1917 test_true(c) ^ test_true(c)
1918 });
1919 assert_system(&mut world, is_true_inc.xnor(is_true_inc), |c| {
1920 !(test_true(c) ^ test_true(c))
1921 });
1922 assert_system(&mut world, is_true_inc.and_then(is_true_inc), |c| {
1923 test_true(c) && test_true(c)
1924 });
1925 assert_system(&mut world, is_true_inc.nand_then(is_true_inc), |c| {
1926 !(test_true(c) && test_true(c))
1927 });
1928
1929 // and where both conditions yield `false`:
1930 assert_system(&mut world, is_false_inc.or_else(is_false_inc), |c| {
1931 test_false(c) || test_false(c)
1932 });
1933 assert_system(&mut world, is_false_inc.nor_else(is_false_inc), |c| {
1934 !(test_false(c) || test_false(c))
1935 });
1936 assert_system(&mut world, is_false_inc.xor(is_false_inc), |c| {
1937 test_false(c) ^ test_false(c)
1938 });
1939 assert_system(&mut world, is_false_inc.xnor(is_false_inc), |c| {
1940 !(test_false(c) ^ test_false(c))
1941 });
1942 assert_system(&mut world, is_false_inc.and_then(is_false_inc), |c| {
1943 test_false(c) && test_false(c)
1944 });
1945 assert_system(&mut world, is_false_inc.nand_then(is_false_inc), |c| {
1946 !(test_false(c) && test_false(c))
1947 });
1948 }
1949
1950 #[test]
1951 fn run_condition_combinators() {
1952 let mut world = World::new();
1953 world.init_resource::<Counter>();
1954 let mut schedule = Schedule::default();
1955
1956 schedule.add_systems(
1957 (
1958 increment_counter.run_if(every_other_time.and_eager(|| true)), // Run every odd cycle.
1959 increment_counter.run_if(every_other_time.nand_eager(|| false)), // Always run.
1960 double_counter.run_if(every_other_time.nor_eager(|| false)), // Run every even cycle.
1961 increment_counter.run_if(every_other_time.or_eager(|| true)), // Always run.
1962 increment_counter.run_if(every_other_time.xnor(|| true)), // Run every odd cycle.
1963 double_counter.run_if(every_other_time.xnor(|| false)), // Run every even cycle.
1964 increment_counter.run_if(every_other_time.xor(|| false)), // Run every odd cycle.
1965 double_counter.run_if(every_other_time.xor(|| true)), // Run every even cycle.
1966 )
1967 .chain(),
1968 );
1969
1970 schedule.run(&mut world);
1971 assert_eq!(world.resource::<Counter>().0, 5);
1972 schedule.run(&mut world);
1973 assert_eq!(world.resource::<Counter>().0, 52);
1974 }
1975
1976 #[test]
1977 fn multiple_run_conditions() {
1978 let mut world = World::new();
1979 world.init_resource::<Counter>();
1980 let mut schedule = Schedule::default();
1981
1982 // Run every other cycle
1983 schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true));
1984 // Never run
1985 schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false));
1986
1987 schedule.run(&mut world);
1988 assert_eq!(world.resource::<Counter>().0, 1);
1989 schedule.run(&mut world);
1990 assert_eq!(world.resource::<Counter>().0, 1);
1991 }
1992
1993 #[test]
1994 fn multiple_run_conditions_is_and_operation() {
1995 let mut world = World::new();
1996 world.init_resource::<Counter>();
1997
1998 let mut schedule = Schedule::default();
1999
2000 // This should never run, if multiple run conditions worked
2001 // like an OR condition then it would always run
2002 schedule.add_systems(
2003 increment_counter
2004 .run_if(every_other_time)
2005 .run_if(not(every_other_time)),
2006 );
2007
2008 schedule.run(&mut world);
2009 assert_eq!(world.resource::<Counter>().0, 0);
2010 schedule.run(&mut world);
2011 assert_eq!(world.resource::<Counter>().0, 0);
2012 }
2013 #[derive(Component)]
2014 struct TestComponent;
2015
2016 #[derive(Message)]
2017 struct TestMessage;
2018
2019 #[derive(Resource)]
2020 struct TestResource(());
2021
2022 fn test_system() {}
2023
2024 // Ensure distributive_run_if compiles with the common conditions.
2025 #[test]
2026 fn distributive_run_if_compiles() {
2027 Schedule::default().add_systems(
2028 (test_system, test_system)
2029 .distributive_run_if(run_once)
2030 .distributive_run_if(resource_exists::<TestResource>)
2031 .distributive_run_if(resource_added::<TestResource>)
2032 .distributive_run_if(resource_changed::<TestResource>)
2033 .distributive_run_if(resource_exists_and_changed::<TestResource>)
2034 .distributive_run_if(resource_changed_or_removed::<TestResource>)
2035 .distributive_run_if(resource_removed::<TestResource>)
2036 .distributive_run_if(on_message::<TestMessage>)
2037 .distributive_run_if(any_with_component::<TestComponent>)
2038 .distributive_run_if(any_match_filter::<With<TestComponent>>)
2039 .distributive_run_if(not(run_once)),
2040 );
2041 }
2042
2043 #[test]
2044 fn run_if_error_contains_system() {
2045 let mut world = World::new();
2046 world.insert_resource(FallbackErrorHandler(my_error_handler));
2047
2048 #[derive(Resource)]
2049 struct MyResource;
2050
2051 fn condition(_res: Res<MyResource>) -> bool {
2052 true
2053 }
2054
2055 fn my_error_handler(_: BevyError, ctx: ErrorContext) {
2056 let a = IntoSystem::into_system(system_a);
2057 let b = IntoSystem::into_system(system_b);
2058 assert!(
2059 matches!(ctx, ErrorContext::RunCondition { system, on_set, .. } if (on_set && system == b.name()) || (!on_set && system == a.name()))
2060 );
2061 }
2062
2063 fn system_a() {}
2064 fn system_b() {}
2065
2066 let mut schedule = Schedule::default();
2067 schedule.add_systems(system_a.run_if(condition));
2068 schedule.run(&mut world);
2069
2070 #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
2071 struct Set;
2072
2073 let mut schedule = Schedule::default();
2074 schedule
2075 .add_systems((system_b,).in_set(Set))
2076 .configure_sets(Set.run_if(condition));
2077 schedule.run(&mut world);
2078 }
2079}