use bevy_ecs::schedule::{Stage, StateData};
use bevy_ecs::world::World;
use bevy_utils::HashMap;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CurrentState<T>(pub T);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NextState<T>(pub T);
pub struct StateTransitionStage<T: StateData> {
enter_stages: HashMap<T, Box<dyn Stage>>,
exit_stages: HashMap<T, Box<dyn Stage>>,
default: T,
}
impl<T: StateData> StateTransitionStage<T> {
pub fn new(default: T) -> Self {
Self {
enter_stages: Default::default(),
exit_stages: Default::default(),
default,
}
}
pub fn set_enter_stage<S: Stage>(&mut self, state: T, stage: S) {
self.enter_stages.insert(state, Box::new(stage));
}
pub fn set_exit_stage<S: Stage>(&mut self, state: T, stage: S) {
self.exit_stages.insert(state, Box::new(stage));
}
pub fn with_enter_stage<S: Stage>(mut self, state: T, stage: S) -> Self {
self.set_enter_stage(state, stage);
self
}
pub fn with_exit_stage<S: Stage>(mut self, state: T, stage: S) -> Self {
self.set_exit_stage(state, stage);
self
}
}
impl<T: StateData> Stage for StateTransitionStage<T> {
fn run(&mut self, world: &mut World) {
loop {
let current = if let Some(res) = world.get_resource::<CurrentState<T>>() {
res.0.clone()
} else {
world.insert_resource(CurrentState(self.default.clone()));
if let Some(stage) = self.enter_stages.get_mut(&self.default) {
stage.run(world);
}
world
.get_resource_or_insert_with(|| CurrentState(self.default.clone()))
.0
.clone()
};
let next = world.remove_resource::<NextState<T>>();
if let Some(NextState(next)) = next {
if current == next {
break;
}
if let Some(stage) = self.exit_stages.get_mut(¤t) {
stage.run(world);
}
world.insert_resource(CurrentState(next.clone()));
if let Some(stage) = self.enter_stages.get_mut(&next) {
stage.run(world);
}
} else {
break;
}
}
}
}