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