allow for multiple states to be popped at once
This commit is contained in:
parent
4d7f84281b
commit
f0930c90d7
|
@ -82,7 +82,7 @@ impl GameState<Game> for SimulationState {
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.context.system.keyboard.is_key_pressed(Scancode::Escape) {
|
if context.context.system.keyboard.is_key_pressed(Scancode::Escape) {
|
||||||
return Some(StateChange::Pop);
|
return Some(StateChange::Pop(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub enum State {
|
||||||
pub enum StateChange<ContextType> {
|
pub enum StateChange<ContextType> {
|
||||||
Push(Box<dyn GameState<ContextType>>),
|
Push(Box<dyn GameState<ContextType>>),
|
||||||
Swap(Box<dyn GameState<ContextType>>),
|
Swap(Box<dyn GameState<ContextType>>),
|
||||||
Pop,
|
Pop(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GameState<ContextType> {
|
pub trait GameState<ContextType> {
|
||||||
|
@ -159,6 +159,7 @@ pub struct States<ContextType> {
|
||||||
states: VecDeque<StateContainer<ContextType>>,
|
states: VecDeque<StateContainer<ContextType>>,
|
||||||
command: Option<StateChange<ContextType>>,
|
command: Option<StateChange<ContextType>>,
|
||||||
pending_state: Option<Box<dyn GameState<ContextType>>>,
|
pending_state: Option<Box<dyn GameState<ContextType>>>,
|
||||||
|
pop_count: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<ContextType> States<ContextType> {
|
impl<ContextType> States<ContextType> {
|
||||||
|
@ -167,6 +168,7 @@ impl<ContextType> States<ContextType> {
|
||||||
states: VecDeque::new(),
|
states: VecDeque::new(),
|
||||||
command: None,
|
command: None,
|
||||||
pending_state: None,
|
pending_state: None,
|
||||||
|
pop_count: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,12 +216,12 @@ impl<ContextType> States<ContextType> {
|
||||||
self.swap_boxed_state(Box::new(state))
|
self.swap_boxed_state(Box::new(state))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self) -> Result<(), StateError> {
|
pub fn pop(&mut self, count: u32) -> Result<(), StateError> {
|
||||||
if !self.can_push_or_pop() {
|
if !self.can_push_or_pop() {
|
||||||
Err(StateError::HasPendingStateChange)
|
Err(StateError::HasPendingStateChange)
|
||||||
} else {
|
} else {
|
||||||
if !self.states.is_empty() {
|
if !self.states.is_empty() {
|
||||||
self.command = Some(StateChange::Pop);
|
self.command = Some(StateChange::Pop(count));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -241,9 +243,10 @@ impl<ContextType> States<ContextType> {
|
||||||
StateChange::Push(new_state) => {
|
StateChange::Push(new_state) => {
|
||||||
self.pending_state = Some(new_state);
|
self.pending_state = Some(new_state);
|
||||||
},
|
},
|
||||||
StateChange::Pop => {
|
StateChange::Pop(count) => {
|
||||||
if let Some(state) = self.states.front_mut() {
|
if let Some(state) = self.states.front_mut() {
|
||||||
state.pending_transition_out(TransitionTo::Dead);
|
state.pending_transition_out(TransitionTo::Dead);
|
||||||
|
self.pop_count = Some(count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StateChange::Swap(new_state) => {
|
StateChange::Swap(new_state) => {
|
||||||
|
@ -285,10 +288,20 @@ impl<ContextType> States<ContextType> {
|
||||||
if let Some(pending_state_change) = state.pending_state_change() {
|
if let Some(pending_state_change) = state.pending_state_change() {
|
||||||
match pending_state_change {
|
match pending_state_change {
|
||||||
State::Dead => {
|
State::Dead => {
|
||||||
state.kill(context)?;
|
if let Some(pop_count) = self.pop_count {
|
||||||
|
// pop the requested amount of states off the top
|
||||||
// remove the dead state
|
for _ in 0..pop_count {
|
||||||
self.states.pop_front();
|
if let Some(mut state) = self.states.pop_front() {
|
||||||
|
state.kill(context)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.pop_count = None;
|
||||||
|
} else {
|
||||||
|
// only need to pop off the top state since it is dead, because it
|
||||||
|
// was swapped out
|
||||||
|
state.kill(context)?;
|
||||||
|
self.states.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
if self.pending_state.is_some() {
|
if self.pending_state.is_some() {
|
||||||
// if there is a new pending state waiting, we can add it here right now
|
// if there is a new pending state waiting, we can add it here right now
|
||||||
|
@ -372,7 +385,7 @@ impl<ContextType> States<ContextType> {
|
||||||
match state_change {
|
match state_change {
|
||||||
StateChange::Push(state) => self.push_boxed_state(state)?,
|
StateChange::Push(state) => self.push_boxed_state(state)?,
|
||||||
StateChange::Swap(state) => self.swap_boxed_state(state)?,
|
StateChange::Swap(state) => self.swap_boxed_state(state)?,
|
||||||
StateChange::Pop => self.pop()?,
|
StateChange::Pop(count) => self.pop(count)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -531,7 +544,7 @@ mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// state begins to transition out to 'dead'
|
// state begins to transition out to 'dead'
|
||||||
|
@ -604,7 +617,7 @@ mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// state begins to transition out to 'dead'
|
// state begins to transition out to 'dead'
|
||||||
|
@ -720,7 +733,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// pop second state
|
// pop second state
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// second state begins to transition out to 'dead'
|
// second state begins to transition out to 'dead'
|
||||||
|
@ -759,7 +772,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// pop first state
|
// pop first state
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// first state begins to transition out to 'dead'
|
// first state begins to transition out to 'dead'
|
||||||
|
@ -899,7 +912,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// pop second state
|
// pop second state
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// second state begins to transition out to 'dead'
|
// second state begins to transition out to 'dead'
|
||||||
|
@ -962,7 +975,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// pop first state
|
// pop first state
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// first state begins to transition out to 'dead'
|
// first state begins to transition out to 'dead'
|
||||||
|
@ -999,6 +1012,118 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pop_multiple_states() -> Result<(), StateError> {
|
||||||
|
use LogEntry::*;
|
||||||
|
use State::*;
|
||||||
|
|
||||||
|
const FIRST: u32 = 1;
|
||||||
|
const SECOND: u32 = 2;
|
||||||
|
|
||||||
|
let mut states = States::<TestContext>::new();
|
||||||
|
let mut context = TestContext::new();
|
||||||
|
|
||||||
|
// push first state
|
||||||
|
states.push(TestState::new(FIRST))?;
|
||||||
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
|
// first state will transition in
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(
|
||||||
|
context.take_log(),
|
||||||
|
vec![
|
||||||
|
StateChange(FIRST, Pending, Dead),
|
||||||
|
StateChange(FIRST, TransitionIn, Pending),
|
||||||
|
Transition(FIRST, TransitionIn),
|
||||||
|
Update(FIRST, TransitionIn),
|
||||||
|
Render(FIRST, TransitionIn)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
// first state finished transitioning in, now moves to active
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(
|
||||||
|
context.take_log(),
|
||||||
|
vec![
|
||||||
|
StateChange(FIRST, Active, TransitionIn),
|
||||||
|
Update(FIRST, Active),
|
||||||
|
Render(FIRST, Active)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// push second state
|
||||||
|
states.push(TestState::new(SECOND))?;
|
||||||
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
|
// first state begins to transition out to 'paused' state
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(
|
||||||
|
context.take_log(),
|
||||||
|
vec![
|
||||||
|
StateChange(FIRST, TransitionOut(TransitionTo::Paused), Active),
|
||||||
|
Transition(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
|
Update(FIRST, TransitionOut(TransitionTo::Paused)),
|
||||||
|
Render(FIRST, TransitionOut(TransitionTo::Paused))
|
||||||
|
]
|
||||||
|
);
|
||||||
|
// state finished transitioning out, now is paused
|
||||||
|
// second state starts up, will transition in
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(
|
||||||
|
context.take_log(),
|
||||||
|
vec![
|
||||||
|
StateChange(FIRST, Paused, TransitionOut(TransitionTo::Paused)),
|
||||||
|
StateChange(SECOND, Pending, Dead),
|
||||||
|
StateChange(SECOND, TransitionIn, Pending),
|
||||||
|
Transition(SECOND, TransitionIn),
|
||||||
|
Update(SECOND, TransitionIn),
|
||||||
|
Render(SECOND, TransitionIn)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
// second state finished transitioning in, now moves to active
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(
|
||||||
|
context.take_log(),
|
||||||
|
vec![
|
||||||
|
StateChange(SECOND, Active, TransitionIn),
|
||||||
|
Update(SECOND, Active),
|
||||||
|
Render(SECOND, Active)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// pop both states
|
||||||
|
states.pop(2)?;
|
||||||
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
|
// second state begins to transition out to 'dead'
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(
|
||||||
|
context.take_log(),
|
||||||
|
vec![
|
||||||
|
StateChange(SECOND, TransitionOut(TransitionTo::Dead), Active),
|
||||||
|
Transition(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
|
Update(SECOND, TransitionOut(TransitionTo::Dead)),
|
||||||
|
Render(SECOND, TransitionOut(TransitionTo::Dead))
|
||||||
|
]
|
||||||
|
);
|
||||||
|
// second state finished transitioning out, now dies.
|
||||||
|
// first state only goes through a state change, paused to dead. no transition
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(
|
||||||
|
context.take_log(),
|
||||||
|
vec![
|
||||||
|
StateChange(SECOND, Dead, TransitionOut(TransitionTo::Dead)),
|
||||||
|
StateChange(FIRST, Dead, Paused)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// nothing! no states anymore!
|
||||||
|
tick(&mut states, &mut context)?;
|
||||||
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn swap_states() -> Result<(), StateError> {
|
fn swap_states() -> Result<(), StateError> {
|
||||||
use LogEntry::*;
|
use LogEntry::*;
|
||||||
|
@ -1077,7 +1202,7 @@ mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// state begins to transition out to 'dead'
|
// state begins to transition out to 'dead'
|
||||||
|
@ -1128,7 +1253,7 @@ mod tests {
|
||||||
if self.push_after == Some(self.counter) {
|
if self.push_after == Some(self.counter) {
|
||||||
return Some(StateChange::Push(Box::new(SelfPushPopState::new(self.id + 1, None, self.pop_after))));
|
return Some(StateChange::Push(Box::new(SelfPushPopState::new(self.id + 1, None, self.pop_after))));
|
||||||
} else if self.pop_after == self.counter {
|
} else if self.pop_after == self.counter {
|
||||||
return Some(StateChange::Pop);
|
return Some(StateChange::Pop(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -1339,7 +1464,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange));
|
assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange));
|
||||||
assert_matches!(states.pop(), Err(StateError::HasPendingStateChange));
|
assert_matches!(states.pop(1), Err(StateError::HasPendingStateChange));
|
||||||
|
|
||||||
// state finished transitioning in, now moves to active
|
// state finished transitioning in, now moves to active
|
||||||
tick(&mut states, &mut context)?;
|
tick(&mut states, &mut context)?;
|
||||||
|
@ -1352,7 +1477,7 @@ mod tests {
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
assert_eq!(context.take_log(), vec![]);
|
assert_eq!(context.take_log(), vec![]);
|
||||||
|
|
||||||
// state begins to transition out to 'dead'
|
// state begins to transition out to 'dead'
|
||||||
|
@ -1368,13 +1493,13 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange));
|
assert_matches!(states.push(TestState::new(123)), Err(StateError::HasPendingStateChange));
|
||||||
assert_matches!(states.pop(), Err(StateError::HasPendingStateChange));
|
assert_matches!(states.pop(1), Err(StateError::HasPendingStateChange));
|
||||||
|
|
||||||
// state finished transitioning out, now dies
|
// state finished transitioning out, now dies
|
||||||
tick(&mut states, &mut context)?;
|
tick(&mut states, &mut context)?;
|
||||||
assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]);
|
assert_eq!(context.take_log(), vec![StateChange(FOO, Dead, TransitionOut(TransitionTo::Dead))]);
|
||||||
|
|
||||||
states.pop()?;
|
states.pop(1)?;
|
||||||
|
|
||||||
// nothing! no states anymore!
|
// nothing! no states anymore!
|
||||||
tick(&mut states, &mut context)?;
|
tick(&mut states, &mut context)?;
|
||||||
|
|
Loading…
Reference in a new issue