use std::any::TypeId; use std::collections::HashMap; use crate::entity::EntityHandle; use crate::world::{Storage, World}; pub trait PlayerState { fn tick_time(&mut self, _delta: f32) {} fn on_enter(&mut self, _world: &mut World, _entity: EntityHandle) {} fn on_exit(&mut self, _world: &mut World, _entity: EntityHandle) {} fn on_update(&mut self, _world: &mut World, _entity: EntityHandle, _delta: f32) {} fn on_physics_update(&mut self, _world: &mut World, _entity: EntityHandle, _delta: f32) {} } struct StateOps { name: &'static str, remove: Box, insert_default: Box, on_enter: Box, on_exit: Box, tick: Box, physics_tick: Box, } struct StateTransition { to_state_id: TypeId, condition: Box bool>, } pub struct StateMachine { state_ops: HashMap, state_transitions: HashMap>, current_state_id: TypeId, } fn short_type_name() -> &'static str { let full = std::any::type_name::(); full.rsplit("::").next().unwrap_or(full) } impl StateMachine { pub fn new() -> Self { Self { state_ops: HashMap::new(), state_transitions: HashMap::new(), current_state_id: TypeId::of::(), } } pub fn register_state( &mut self, storage: fn(&mut World) -> &mut Storage, ) { let ops = StateOps { name: short_type_name::(), remove: Box::new(move |world, entity| { storage(world).components.remove(&entity); }), insert_default: Box::new(move |world, entity| { storage(world).insert(entity, S::default()); }), on_enter: Box::new(move |world, entity| { if let Some(mut state) = storage(world).components.remove(&entity) { state.on_enter(world, entity); storage(world).insert(entity, state); } }), on_exit: Box::new(move |world, entity| { if let Some(mut state) = storage(world).components.remove(&entity) { state.on_exit(world, entity); storage(world).insert(entity, state); } }), tick: Box::new(move |world, entity, delta| { if let Some(mut state) = storage(world).components.remove(&entity) { state.tick_time(delta); state.on_update(world, entity, delta); storage(world).insert(entity, state); } }), physics_tick: Box::new(move |world, entity, delta| { if let Some(mut state) = storage(world).components.remove(&entity) { state.on_physics_update(world, entity, delta); storage(world).insert(entity, state); } }), }; self.state_ops.insert(TypeId::of::(), ops); } pub fn update(&mut self, world: &mut World, entity: EntityHandle, delta: f32) { if let Some(next_state_id) = self.get_transition_state_id(world) { self.apply_transition(world, entity, next_state_id); } if let Some(ops) = self.state_ops.get(&self.current_state_id) { (ops.tick)(world, entity, delta); } } pub fn physics_update(&mut self, world: &mut World, entity: EntityHandle, delta: f32) { if let Some(ops) = self.state_ops.get(&self.current_state_id) { (ops.physics_tick)(world, entity, 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 apply_transition(&mut self, world: &mut World, entity: EntityHandle, next_id: TypeId) { if let Some(ops) = self.state_ops.get(&self.current_state_id) { (ops.on_exit)(world, entity); (ops.remove)(world, entity); } self.current_state_id = next_id; if let Some(ops) = self.state_ops.get(&next_id) { (ops.insert_default)(world, entity); (ops.on_enter)(world, entity); } } 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_current_state_name(&self) -> &'static str { self.state_ops .get(&self.current_state_id) .map(|ops| ops.name) .unwrap_or("Unknown") } }