Skip to content

Instantly share code, notes, and snippets.

@not-fl3
Created October 11, 2020 15:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save not-fl3/7ba109eb72f9ad28fa33258a233f9bb3 to your computer and use it in GitHub Desktop.
Save not-fl3/7ba109eb72f9ad28fa33258a233f9bb3 to your computer and use it in GitHub Desktop.
use crate::{Player, Room};
use macroquad::coroutines::Coroutine;
type UpdateFn<T, U> = Box<dyn FnMut(&mut T, &mut U, f32)>;
type CoroutineFn<T, U> = Box<dyn FnMut(&mut T, &mut U) -> Coroutine>;
type OnEndFn<T, U> = Box<dyn FnMut(&mut T, &mut U)>;
pub struct State<T, U> {
update: Option<UpdateFn<T, U>>,
coroutine: Option<CoroutineFn<T, U>>,
on_end: Option<OnEndFn<T, U>>
}
impl<T, U> State<T, U> {
pub fn new() -> Self {
State {
update: None,
coroutine: None,
on_end: None
}
}
pub fn update(self, update: impl FnMut(&mut T, &mut U, f32) + 'static) -> Self {
State {
update: Some(Box::new(update)),
..self
}
}
pub fn coroutine(self, coroutine: impl FnMut(&mut T, &mut U) -> Coroutine + 'static) -> Self {
State {
coroutine: Some(Box::new(coroutine)),
..self
}
}
pub fn on_end(self, on_end: impl FnMut(&mut T, &mut U) + 'static) -> Self {
State {
on_end: Some(Box::new(on_end)),
..self
}
}
}
pub enum StateMachineContainer<T, U> {
Ready(StateMachine<T, U>),
InUse {
next_state: Option<usize>,
current_state: usize,
},
}
impl<T, U> StateMachineContainer<T, U> {
pub fn take(&mut self) -> StateMachine<T, U> {
let current_state = self.state();
match std::mem::replace(
self,
StateMachineContainer::InUse {
next_state: None,
current_state,
},
) {
StateMachineContainer::InUse { .. } => panic!(),
StateMachineContainer::Ready(state_machine) => state_machine,
}
}
pub fn put_back(&mut self, mut state_machine: StateMachine<T, U>) {
match self {
StateMachineContainer::Ready(_) => panic!(),
StateMachineContainer::InUse { next_state, .. } => {
if let Some(next_state) = next_state {
state_machine.set_state(*next_state);
}
}
}
*self = StateMachineContainer::Ready(state_machine);
}
pub fn set_state(&mut self, state: usize) {
match self {
StateMachineContainer::Ready(state_machine) => {
state_machine.set_state(state);
}
StateMachineContainer::InUse {
ref mut next_state, ..
} => {
*next_state = Some(state);
}
}
}
pub fn state(&self) -> usize {
match self {
StateMachineContainer::Ready(state_machine) => state_machine.state(),
StateMachineContainer::InUse {
ref current_state, ..
} => *current_state,
}
}
}
pub struct StateMachine<T, U> {
states: Vec<State<T, U>>,
next_state: Option<usize>,
current_state: usize,
}
impl<T, U> StateMachine<T, U> {
const MAX_STATE: usize = 32;
pub fn new() -> Self {
let mut states = vec![];
for _ in 0..Self::MAX_STATE {
states.push(State::new());
}
StateMachine {
states,
next_state: None,
current_state: 0,
}
}
pub fn insert(&mut self, id: usize, state: State<T, U>) {
assert!(id < Self::MAX_STATE);
self.states[id] = state;
}
pub fn set_state(&mut self, state: usize) {
self.next_state = Some(state);
}
pub fn state(&self) -> usize {
self.current_state
}
pub fn update(&mut self, player: &mut T, room: &mut U, dt: f32) {
if let Some(next_state) = self.next_state {
if next_state != self.current_state {
if let Some(on_end) = &mut self.states[self.current_state].on_end {
on_end(player, room);
}
if let Some(coroutine) = &mut self.states[next_state].coroutine {
coroutine(player, room);
}
}
self.current_state = next_state;
self.next_state = None;
}
if let Some(update) = self.states[self.current_state].update.as_mut() {
(update)(player, room, dt);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment