use amethyst_input::is_close_requested;
use ecs::prelude::World;
use std::fmt::Result as FmtResult;
use std::fmt::{Display, Formatter};
use {GameData, StateEvent};
#[derive(Debug)]
pub enum StateError {
NoStatesPresent,
}
impl Display for StateError {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
match *self {
StateError::NoStatesPresent => write!(
fmt,
"Tried to start state machine without any states present"
),
}
}
}
pub struct StateData<'a, T>
where
T: 'a,
{
pub world: &'a mut World,
pub data: &'a mut T,
}
impl<'a, T> StateData<'a, T>
where
T: 'a,
{
pub fn new(world: &'a mut World, data: &'a mut T) -> Self {
StateData { world, data }
}
}
pub enum Trans<T, E> {
None,
Pop,
Push(Box<State<T, E>>),
Switch(Box<State<T, E>>),
Quit,
}
pub type EmptyTrans = Trans<(), StateEvent>;
pub type SimpleTrans<'a, 'b> = Trans<GameData<'a, 'b>, StateEvent>;
pub trait State<T, E: Send + Sync + 'static> {
fn on_start(&mut self, _data: StateData<T>) {}
fn on_stop(&mut self, _data: StateData<T>) {}
fn on_pause(&mut self, _data: StateData<T>) {}
fn on_resume(&mut self, _data: StateData<T>) {}
fn handle_event(&mut self, _data: StateData<T>, _event: E) -> Trans<T, E> {
Trans::None
}
fn fixed_update(&mut self, _data: StateData<T>) -> Trans<T, E> {
Trans::None
}
fn update(&mut self, _data: StateData<T>) -> Trans<T, E> {
Trans::None
}
fn shadow_fixed_update(&mut self, _data: StateData<T>) {}
fn shadow_update(&mut self, _data: StateData<T>) {}
}
pub trait EmptyState {
fn on_start(&mut self, _data: StateData<()>) {}
fn on_stop(&mut self, _data: StateData<()>) {}
fn on_pause(&mut self, _data: StateData<()>) {}
fn on_resume(&mut self, _data: StateData<()>) {}
fn handle_event(&mut self, _data: StateData<()>, event: StateEvent) -> EmptyTrans {
if let StateEvent::Window(event) = &event {
if is_close_requested(&event) {
Trans::Quit
} else {
Trans::None
}
} else {
Trans::None
}
}
fn fixed_update(&mut self, _data: StateData<()>) -> EmptyTrans {
Trans::None
}
fn update(&mut self, _data: StateData<()>) -> EmptyTrans {
Trans::None
}
fn shadow_fixed_update(&mut self, _data: StateData<()>) {}
fn shadow_update(&mut self, _data: StateData<()>) {}
}
impl<T: EmptyState> State<(), StateEvent> for T {
fn on_start(&mut self, data: StateData<()>) {
self.on_start(data)
}
fn on_stop(&mut self, data: StateData<()>) {
self.on_stop(data)
}
fn on_pause(&mut self, data: StateData<()>) {
self.on_pause(data)
}
fn on_resume(&mut self, data: StateData<()>) {
self.on_resume(data)
}
fn handle_event(&mut self, data: StateData<()>, event: StateEvent) -> EmptyTrans {
self.handle_event(data, event)
}
fn fixed_update(&mut self, data: StateData<()>) -> EmptyTrans {
self.fixed_update(data)
}
fn update(&mut self, data: StateData<()>) -> EmptyTrans {
self.update(data)
}
fn shadow_fixed_update(&mut self, data: StateData<()>) {
self.shadow_fixed_update(data);
}
fn shadow_update(&mut self, data: StateData<()>) {
self.shadow_update(data);
}
}
pub trait SimpleState<'a, 'b> {
fn on_start(&mut self, _data: StateData<GameData>) {}
fn on_stop(&mut self, _data: StateData<GameData>) {}
fn on_pause(&mut self, _data: StateData<GameData>) {}
fn on_resume(&mut self, _data: StateData<GameData>) {}
fn handle_event(
&mut self,
_data: StateData<GameData>,
event: StateEvent,
) -> SimpleTrans<'a, 'b> {
if let StateEvent::Window(event) = &event {
if is_close_requested(&event) {
Trans::Quit
} else {
Trans::None
}
} else {
Trans::None
}
}
fn fixed_update(&mut self, _data: StateData<GameData>) -> SimpleTrans<'a, 'b> {
Trans::None
}
fn update(&mut self, _data: &mut StateData<GameData>) -> SimpleTrans<'a, 'b> {
Trans::None
}
fn shadow_fixed_update(&mut self, _data: StateData<GameData>) {}
fn shadow_update(&mut self, _data: StateData<GameData>) {}
}
impl<'a, 'b, T: SimpleState<'a, 'b>> State<GameData<'a, 'b>, StateEvent> for T {
fn on_start(&mut self, data: StateData<GameData>) {
self.on_start(data)
}
fn on_stop(&mut self, data: StateData<GameData>) {
self.on_stop(data)
}
fn on_pause(&mut self, data: StateData<GameData>) {
self.on_pause(data)
}
fn on_resume(&mut self, data: StateData<GameData>) {
self.on_resume(data)
}
fn handle_event(
&mut self,
data: StateData<GameData>,
event: StateEvent,
) -> SimpleTrans<'a, 'b> {
self.handle_event(data, event)
}
fn fixed_update(&mut self, data: StateData<GameData>) -> SimpleTrans<'a, 'b> {
self.fixed_update(data)
}
fn update(&mut self, mut data: StateData<GameData>) -> SimpleTrans<'a, 'b> {
let r = self.update(&mut data);
data.data.update(&data.world);
r
}
fn shadow_fixed_update(&mut self, data: StateData<GameData>) {
self.shadow_fixed_update(data);
}
fn shadow_update(&mut self, data: StateData<GameData>) {
self.shadow_update(data);
}
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct StateMachine<'a, T, E> {
running: bool,
#[derivative(Debug = "ignore")]
state_stack: Vec<Box<State<T, E> + 'a>>,
}
impl<'a, T, E: Send + Sync + 'static> StateMachine<'a, T, E> {
pub fn new<S: State<T, E> + 'a>(initial_state: S) -> StateMachine<'a, T, E> {
StateMachine {
running: false,
state_stack: vec![Box::new(initial_state)],
}
}
pub fn is_running(&self) -> bool {
self.running
}
pub fn start(&mut self, data: StateData<T>) -> Result<(), StateError> {
if !self.running {
let state = self
.state_stack
.last_mut()
.ok_or(StateError::NoStatesPresent)?;
state.on_start(data);
self.running = true;
}
Ok(())
}
pub fn handle_event(&mut self, data: StateData<T>, event: E) {
let StateData { world, data } = data;
if self.running {
let trans = match self.state_stack.last_mut() {
Some(state) => state.handle_event(StateData { world, data }, event),
None => Trans::None,
};
self.transition(trans, StateData { world, data });
}
}
pub fn fixed_update(&mut self, data: StateData<T>) {
let StateData { world, data } = data;
if self.running {
let trans = match self.state_stack.last_mut() {
Some(state) => state.fixed_update(StateData { world, data }),
None => Trans::None,
};
for state in self.state_stack.iter_mut() {
state.shadow_fixed_update(StateData { world, data });
}
self.transition(trans, StateData { world, data });
}
}
pub fn update(&mut self, data: StateData<T>) {
let StateData { world, data } = data;
if self.running {
let trans = match self.state_stack.last_mut() {
Some(state) => state.update(StateData { world, data }),
None => Trans::None,
};
for state in self.state_stack.iter_mut() {
state.shadow_update(StateData { world, data });
}
self.transition(trans, StateData { world, data });
}
}
fn transition(&mut self, request: Trans<T, E>, data: StateData<T>) {
if self.running {
match request {
Trans::None => (),
Trans::Pop => self.pop(data),
Trans::Push(state) => self.push(state, data),
Trans::Switch(state) => self.switch(state, data),
Trans::Quit => self.stop(data),
}
}
}
fn switch(&mut self, state: Box<State<T, E>>, data: StateData<T>) {
if self.running {
let StateData { world, data } = data;
if let Some(mut state) = self.state_stack.pop() {
state.on_stop(StateData { world, data });
}
self.state_stack.push(state);
let state = self.state_stack.last_mut().unwrap();
state.on_start(StateData { world, data });
}
}
fn push(&mut self, state: Box<State<T, E>>, data: StateData<T>) {
if self.running {
let StateData { world, data } = data;
if let Some(state) = self.state_stack.last_mut() {
state.on_pause(StateData { world, data });
}
self.state_stack.push(state);
let state = self.state_stack.last_mut().unwrap();
state.on_start(StateData { world, data });
}
}
fn pop(&mut self, data: StateData<T>) {
if self.running {
let StateData { world, data } = data;
if let Some(mut state) = self.state_stack.pop() {
state.on_stop(StateData { world, data });
}
if let Some(state) = self.state_stack.last_mut() {
state.on_resume(StateData { world, data });
} else {
self.running = false;
}
}
}
pub(crate) fn stop(&mut self, data: StateData<T>) {
if self.running {
let StateData { world, data } = data;
while let Some(mut state) = self.state_stack.pop() {
state.on_stop(StateData { world, data });
}
self.running = false;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct State1(u8);
struct State2;
impl State<(), ()> for State1 {
fn update(&mut self, _: StateData<()>) -> Trans<(), ()> {
if self.0 > 0 {
self.0 -= 1;
Trans::None
} else {
Trans::Switch(Box::new(State2))
}
}
}
impl State<(), ()> for State2 {
fn update(&mut self, _: StateData<()>) -> Trans<(), ()> {
Trans::Pop
}
}
#[test]
fn switch_pop() {
use ecs::prelude::World;
let mut world = World::new();
let mut sm = StateMachine::new(State1(7));
sm.start(StateData::new(&mut world, &mut ())).unwrap();
for _ in 0..8 {
sm.update(StateData::new(&mut world, &mut ()));
assert!(sm.is_running());
}
sm.update(StateData::new(&mut world, &mut ()));
assert!(!sm.is_running());
}
}