use crate::state::{State, States};1use bevy_ecs::{change_detection::DetectChanges, system::Res};23/// A [`SystemCondition`](bevy_ecs::prelude::SystemCondition)-satisfying system that returns `true`4/// if the state machine exists.5///6/// # Example7///8/// ```9/// # use bevy_ecs::prelude::*;10/// # use bevy_state::prelude::*;11/// # use bevy_app::{App, Update};12/// # use bevy_state::app::StatesPlugin;13/// # #[derive(Resource, Default)]14/// # struct Counter(u8);15/// # let mut app = App::new();16/// # app17/// # .init_resource::<Counter>()18/// # .add_plugins(StatesPlugin);19/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]20/// enum GameState {21/// #[default]22/// Playing,23/// Paused,24/// }25///26/// app.add_systems(Update,27/// // `state_exists` will only return true if the28/// // given state exists29/// my_system.run_if(state_exists::<GameState>),30/// );31///32/// fn my_system(mut counter: ResMut<Counter>) {33/// counter.0 += 1;34/// }35///36/// // `GameState` does not yet exist so `my_system` won't run37/// app.update();38/// assert_eq!(app.world().resource::<Counter>().0, 0);39///40/// app.init_state::<GameState>();41///42/// // `GameState` now exists so `my_system` will run43/// app.update();44/// assert_eq!(app.world().resource::<Counter>().0, 1);45/// ```46pub fn state_exists<S: States>(current_state: Option<Res<State<S>>>) -> bool {47current_state.is_some()48}4950/// Generates a [`SystemCondition`](bevy_ecs::prelude::SystemCondition)-satisfying closure that returns `true`51/// if the state machine is currently in `state`.52///53/// Will return `false` if the state does not exist or if not in `state`.54///55/// # Example56///57/// ```58/// # use bevy_ecs::prelude::*;59/// # use bevy_state::prelude::*;60/// # use bevy_app::{App, Update};61/// # use bevy_state::app::StatesPlugin;62/// # #[derive(Resource, Default)]63/// # struct Counter(u8);64/// # let mut app = App::new();65/// # app66/// # .init_resource::<Counter>()67/// # .add_plugins(StatesPlugin);68/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]69/// enum GameState {70/// #[default]71/// Playing,72/// Paused,73/// }74///75/// app76/// .init_state::<GameState>()77/// .add_systems(Update, (78/// // `in_state` will only return true if the79/// // given state equals the given value80/// play_system.run_if(in_state(GameState::Playing)),81/// pause_system.run_if(in_state(GameState::Paused)),82/// ));83///84/// fn play_system(mut counter: ResMut<Counter>) {85/// counter.0 += 1;86/// }87///88/// fn pause_system(mut counter: ResMut<Counter>) {89/// counter.0 -= 1;90/// }91///92/// // We default to `GameState::Playing` so `play_system` runs93/// app.update();94/// assert_eq!(app.world().resource::<Counter>().0, 1);95///96/// app.insert_state(GameState::Paused);97///98/// // Now that we are in `GameState::Pause`, `pause_system` will run99/// app.update();100/// assert_eq!(app.world().resource::<Counter>().0, 0);101/// ```102pub fn in_state<S: States>(state: S) -> impl FnMut(Option<Res<State<S>>>) -> bool + Clone {103move |current_state: Option<Res<State<S>>>| match current_state {104Some(current_state) => *current_state == state,105None => false,106}107}108109/// A [`SystemCondition`](bevy_ecs::prelude::SystemCondition)-satisfying system that returns `true`110/// if the state machine changed state.111///112/// To do things on transitions to/from specific states, use their respective OnEnter/OnExit113/// schedules. Use this run condition if you want to detect any change, regardless of the value.114///115/// Returns false if the state does not exist or the state has not changed.116///117/// # Example118///119/// ```120/// # use bevy_ecs::prelude::*;121/// # use bevy_state::prelude::*;122/// # use bevy_state::app::StatesPlugin;123/// # use bevy_app::{App, Update};124/// # #[derive(Resource, Default)]125/// # struct Counter(u8);126/// # let mut app = App::new();127/// # app128/// # .init_resource::<Counter>()129/// # .add_plugins(StatesPlugin);130/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]131/// enum GameState {132/// #[default]133/// Playing,134/// Paused,135/// }136///137/// app138/// .init_state::<GameState>()139/// .add_systems(Update,140/// // `state_changed` will only return true if the141/// // given states value has just been updated or142/// // the state has just been added143/// my_system.run_if(state_changed::<GameState>),144/// );145///146/// fn my_system(mut counter: ResMut<Counter>) {147/// counter.0 += 1;148/// }149///150/// // `GameState` has just been added so `my_system` will run151/// app.update();152/// assert_eq!(app.world().resource::<Counter>().0, 1);153///154/// // `GameState` has not been updated so `my_system` will not run155/// app.update();156/// assert_eq!(app.world().resource::<Counter>().0, 1);157///158/// app.insert_state(GameState::Paused);159///160/// // Now that `GameState` has been updated `my_system` will run161/// app.update();162/// assert_eq!(app.world().resource::<Counter>().0, 2);163/// ```164pub fn state_changed<S: States>(current_state: Option<Res<State<S>>>) -> bool {165let Some(current_state) = current_state else {166return false;167};168current_state.is_changed()169}170171#[cfg(test)]172mod tests {173use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule, SystemCondition};174175use crate::prelude::*;176use bevy_state_macros::States;177178#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]179enum TestState {180#[default]181A,182B,183}184185fn test_system() {}186187// Ensure distributive_run_if compiles with the common conditions.188#[test]189fn distributive_run_if_compiles() {190Schedule::default().add_systems(191(test_system, test_system)192.distributive_run_if(state_exists::<TestState>)193.distributive_run_if(in_state(TestState::A).or(in_state(TestState::B)))194.distributive_run_if(state_changed::<TestState>),195);196}197}198199200