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