use std::any::{Any, TypeId}; use std::collections::HashMap; use crate::world::World; pub trait StateAgent {} pub trait State: Any { fn get_state_name(&self) -> &'static str; fn on_state_enter(&mut self, world: &mut World) {} fn on_state_exit(&mut self, world: &mut World) {} fn on_state_update(&mut self, world: &mut World, delta: f32) {} fn on_state_physics_update(&mut self, world: &mut World, delta: f32) {} } impl dyn State { fn dyn_type_id(&self) -> std::any::TypeId { Any::type_id(self) } } pub struct StateTransition { to_state_id: TypeId, condition: Box bool>, } pub struct StateMachine { state_transitions: HashMap>, current_state_id: TypeId, states: HashMap>, pub time_in_state: f32, } impl StateMachine { pub fn new(enter_state: Box) -> Self { let state_id = enter_state.dyn_type_id(); let mut states = HashMap::new(); states.insert(state_id, enter_state); Self { state_transitions: HashMap::new(), current_state_id: state_id, states, time_in_state: 0.0, } } pub fn update(&mut self, world: &mut World, delta: f32) { if let Some(next_state_id) = self.get_transition_state_id(world) { self.time_in_state = 0.0; self.transition_to(world, next_state_id); } if let Some(current_state) = self.states.get_mut(&self.current_state_id) { current_state.on_state_update(world, delta); } self.time_in_state += delta; } fn get_transition_state_id(&self, world: &World) -> Option { if let Some(transitions) = self.state_transitions.get(&self.current_state_id) { for transition in transitions { if (transition.condition)(world) { return Some(transition.to_state_id); } } } None } fn transition_to(&mut self, world: &mut World, new_state_id: TypeId) { if let Some(current_state) = self.states.get_mut(&self.current_state_id) { current_state.on_state_exit(world); } self.current_state_id = new_state_id; if let Some(new_state) = self.states.get_mut(&self.current_state_id) { new_state.on_state_enter(world); } } pub fn get_current_state(&self) -> Option<&dyn State> { self.states.get(&self.current_state_id).map(|b| b.as_ref()) } pub fn get_current_state_mut(&mut self) -> Option<&mut dyn State> { self.states .get_mut(&self.current_state_id) .map(|b| b.as_mut()) } pub fn add_state(&mut self, state: T) { let state_id = TypeId::of::(); self.states.insert(state_id, Box::new(state)); } pub fn add_transition( &mut self, condition: impl Fn(&World) -> bool + 'static, ) { let from_id = TypeId::of::(); let to_id = TypeId::of::(); let transitions = self.state_transitions.entry(from_id).or_default(); transitions.push(StateTransition { to_state_id: to_id, condition: Box::new(condition), }); } pub fn get_available_transitions_count(&self) -> usize { self.state_transitions .get(&self.current_state_id) .map(|transitions| transitions.len()) .unwrap_or(0) } }