more reorganizing
This commit is contained in:
2
src/states/mod.rs
Normal file
2
src/states/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod state;
|
||||
pub mod player_states;
|
||||
496
src/states/player_states.rs
Normal file
496
src/states/player_states.rs
Normal file
@@ -0,0 +1,496 @@
|
||||
use glam::Vec3;
|
||||
use kurbo::ParamCurve;
|
||||
use rapier3d::math::Vector;
|
||||
|
||||
use crate::components::player_states::{
|
||||
FallingState, IdleState, JumpingState, LeapingState, RollingState, WalkingState,
|
||||
};
|
||||
use crate::entity::EntityHandle;
|
||||
use crate::physics::PhysicsManager;
|
||||
use crate::states::state::State;
|
||||
use crate::world::World;
|
||||
|
||||
pub const LEAP_DURATION: f32 = 0.18;
|
||||
pub const ROLL_DURATION: f32 = 0.42;
|
||||
const LEAP_SPEED: f32 = 18.0;
|
||||
const ROLL_SPEED: f32 = 14.0;
|
||||
|
||||
impl State for IdleState
|
||||
{
|
||||
fn tick_time(&mut self, delta: f32)
|
||||
{
|
||||
self.time_in_state += delta;
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, world: &mut World, entity: EntityHandle)
|
||||
{
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let current_velocity = *rigidbody.linvel();
|
||||
let idle_damping = world
|
||||
.movements
|
||||
.with(entity, |m| m.idle_damping)
|
||||
.unwrap_or(0.1);
|
||||
|
||||
let horizontal_velocity = Vec3::new(current_velocity.x, 0.0, current_velocity.z);
|
||||
let new_horizontal_velocity = horizontal_velocity * idle_damping;
|
||||
|
||||
rigidbody.set_linvel(
|
||||
Vector::new(
|
||||
new_horizontal_velocity.x,
|
||||
current_velocity.y,
|
||||
new_horizontal_velocity.z,
|
||||
),
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn on_physics_update(&mut self, world: &mut World, entity: EntityHandle, _delta: f32)
|
||||
{
|
||||
const GROUND_CHECK_DISTANCE: f32 = 0.6;
|
||||
|
||||
let current_translation = world
|
||||
.physics
|
||||
.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
*rigidbody.translation()
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.unwrap();
|
||||
|
||||
let terrain_height =
|
||||
PhysicsManager::get_terrain_height_at(current_translation.x, current_translation.z);
|
||||
|
||||
if let Some(height) = terrain_height
|
||||
{
|
||||
let target_y = height + 1.0;
|
||||
let distance_to_ground = current_translation.y - target_y;
|
||||
|
||||
if distance_to_ground.abs() < GROUND_CHECK_DISTANCE
|
||||
{
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let next_translation =
|
||||
Vector::new(current_translation.x, target_y, current_translation.z);
|
||||
rigidbody.set_next_kinematic_translation(next_translation);
|
||||
});
|
||||
});
|
||||
|
||||
world.movements.with_mut(entity, |movement| {
|
||||
movement.movement_context.is_floored = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for WalkingState
|
||||
{
|
||||
fn tick_time(&mut self, delta: f32)
|
||||
{
|
||||
self.time_in_state += delta;
|
||||
}
|
||||
|
||||
fn on_physics_update(&mut self, world: &mut World, entity: EntityHandle, delta: f32)
|
||||
{
|
||||
let (movement_input, walking_config) = world
|
||||
.movements
|
||||
.with(entity, |movement| {
|
||||
let input = world
|
||||
.inputs
|
||||
.with(entity, |input| input.move_direction)
|
||||
.unwrap_or(Vec3::ZERO);
|
||||
(input, movement.clone())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let t = (self.time_in_state / walking_config.walking_acceleration_duration).clamp(0.0, 1.0);
|
||||
let acceleration_amount = walking_config.walking_acceleration_curve.eval(t as f64).y as f32;
|
||||
|
||||
let current_translation = world
|
||||
.physics
|
||||
.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
*rigidbody.translation()
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.unwrap();
|
||||
|
||||
let terrain_height =
|
||||
PhysicsManager::get_terrain_height_at(current_translation.x, current_translation.z);
|
||||
|
||||
let target_y = if let Some(height) = terrain_height
|
||||
{
|
||||
height + 1.0
|
||||
}
|
||||
else
|
||||
{
|
||||
current_translation.y
|
||||
};
|
||||
|
||||
let slope_angle = if movement_input.length_squared() > 0.01
|
||||
{
|
||||
PhysicsManager::get_terrain_slope_in_direction(
|
||||
current_translation.x,
|
||||
current_translation.z,
|
||||
movement_input.x,
|
||||
movement_input.z,
|
||||
)
|
||||
}
|
||||
else
|
||||
{
|
||||
0.0
|
||||
};
|
||||
|
||||
let slope_multiplier = {
|
||||
const MAX_SLOPE_ANGLE: f32 = std::f32::consts::PI / 4.0;
|
||||
|
||||
if slope_angle > 0.0
|
||||
{
|
||||
let uphill_factor = (slope_angle / MAX_SLOPE_ANGLE).min(1.0);
|
||||
1.0 - uphill_factor * 0.9
|
||||
}
|
||||
else
|
||||
{
|
||||
let downhill_factor = (slope_angle.abs() / MAX_SLOPE_ANGLE).min(1.0);
|
||||
1.0 + downhill_factor * 0.5
|
||||
}
|
||||
};
|
||||
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let current_velocity = *rigidbody.linvel();
|
||||
|
||||
let horizontal_velocity = Vec3::new(current_velocity.x, 0.0, current_velocity.z);
|
||||
|
||||
let walking_force = movement_input
|
||||
* walking_config.walking_acceleration
|
||||
* delta
|
||||
* acceleration_amount;
|
||||
|
||||
let new_horizontal_velocity = (walking_force
|
||||
+ horizontal_velocity * walking_config.walking_damping)
|
||||
.clamp_length_max(walking_config.max_walking_speed * slope_multiplier);
|
||||
|
||||
let next_translation = Vector::new(
|
||||
current_translation.x + new_horizontal_velocity.x * delta,
|
||||
target_y,
|
||||
current_translation.z + new_horizontal_velocity.z * delta,
|
||||
);
|
||||
|
||||
rigidbody.set_linvel(
|
||||
Vector::new(new_horizontal_velocity.x, 0.0, new_horizontal_velocity.z),
|
||||
true,
|
||||
);
|
||||
rigidbody.set_next_kinematic_translation(next_translation);
|
||||
});
|
||||
});
|
||||
|
||||
world.movements.with_mut(entity, |movement| {
|
||||
movement.movement_context.is_floored = terrain_height.is_some();
|
||||
});
|
||||
|
||||
if movement_input.length_squared() > 0.1
|
||||
{
|
||||
world.transforms.with_mut(entity, |transform| {
|
||||
let target_rotation = f32::atan2(movement_input.x, movement_input.z);
|
||||
transform.rotation.y = target_rotation;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State for JumpingState
|
||||
{
|
||||
fn tick_time(&mut self, delta: f32)
|
||||
{
|
||||
self.time_in_state += delta;
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, world: &mut World, entity: EntityHandle)
|
||||
{
|
||||
self.time_in_state = 0.0;
|
||||
|
||||
let current_position = world.transforms.get(entity).unwrap().position;
|
||||
|
||||
world.jumps.with_mut(entity, |jump| {
|
||||
jump.jump_context.in_progress = true;
|
||||
jump.jump_context.origin_height = current_position.y;
|
||||
jump.jump_context.duration = 0.0;
|
||||
jump.jump_context.normal = Vec3::Y;
|
||||
});
|
||||
}
|
||||
|
||||
fn on_exit(&mut self, world: &mut World, entity: EntityHandle)
|
||||
{
|
||||
world.jumps.with_mut(entity, |jump| {
|
||||
jump.jump_context.in_progress = false;
|
||||
jump.jump_context.duration = 0.0;
|
||||
});
|
||||
}
|
||||
|
||||
fn on_physics_update(&mut self, world: &mut World, entity: EntityHandle, delta: f32)
|
||||
{
|
||||
world.jumps.with_mut(entity, |jump| {
|
||||
jump.jump_context.duration = self.time_in_state;
|
||||
});
|
||||
|
||||
let jump = world.jumps.with(entity, |jump| jump.clone()).unwrap();
|
||||
|
||||
let normalized_time = (self.time_in_state / jump.jump_duration).min(1.0);
|
||||
let height_progress = jump.jump_curve.eval(normalized_time as f64).y as f32;
|
||||
|
||||
let origin_height = jump.jump_context.origin_height;
|
||||
let target_height = origin_height + height_progress * jump.jump_height;
|
||||
|
||||
let current_translation = world
|
||||
.physics
|
||||
.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
*rigidbody.translation()
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.unwrap();
|
||||
|
||||
let height_diff = target_height - current_translation.y;
|
||||
let required_velocity = height_diff / delta;
|
||||
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let current_velocity = *rigidbody.linvel();
|
||||
let next_translation = Vector::new(
|
||||
current_translation.x + current_velocity.x * delta,
|
||||
current_translation.y + required_velocity * delta,
|
||||
current_translation.z + current_velocity.z * delta,
|
||||
);
|
||||
|
||||
rigidbody.set_linvel(
|
||||
Vector::new(current_velocity.x, required_velocity, current_velocity.z),
|
||||
true,
|
||||
);
|
||||
rigidbody.set_next_kinematic_translation(next_translation);
|
||||
});
|
||||
});
|
||||
|
||||
world.movements.with_mut(entity, |movement| {
|
||||
movement.movement_context.is_floored = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl State for FallingState
|
||||
{
|
||||
fn tick_time(&mut self, delta: f32)
|
||||
{
|
||||
self.time_in_state += delta;
|
||||
}
|
||||
|
||||
fn on_physics_update(&mut self, world: &mut World, entity: EntityHandle, delta: f32)
|
||||
{
|
||||
const GRAVITY: f32 = -9.81 * 5.0;
|
||||
const GROUND_CHECK_DISTANCE: f32 = 0.6;
|
||||
|
||||
let (current_pos, velocity) = world
|
||||
.physics
|
||||
.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let mut vel = *rigidbody.linvel();
|
||||
vel.y += GRAVITY * delta;
|
||||
(*rigidbody.translation(), vel)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.unwrap();
|
||||
|
||||
let next_pos = current_pos + velocity;
|
||||
let terrain_height = PhysicsManager::get_terrain_height_at(next_pos.x, next_pos.z);
|
||||
|
||||
let is_grounded = if let Some(height) = terrain_height
|
||||
{
|
||||
let target_y = height + 1.0;
|
||||
let distance_to_ground = current_pos.y - target_y;
|
||||
|
||||
if distance_to_ground < GROUND_CHECK_DISTANCE && velocity.y <= 0.01
|
||||
{
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let next_pos = Vector::new(current_pos.x, target_y, current_pos.z);
|
||||
rigidbody.set_next_kinematic_translation(next_pos);
|
||||
rigidbody.set_linvel(Vector::new(velocity.x, 0.0, velocity.z), true);
|
||||
});
|
||||
});
|
||||
true
|
||||
}
|
||||
else
|
||||
{
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let next_pos = current_pos + velocity * delta;
|
||||
rigidbody.set_next_kinematic_translation(next_pos);
|
||||
rigidbody.set_linvel(velocity, true);
|
||||
});
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
||||
let next_pos = current_pos + velocity * delta;
|
||||
rigidbody.set_next_kinematic_translation(next_pos);
|
||||
rigidbody.set_linvel(velocity, true);
|
||||
});
|
||||
});
|
||||
false
|
||||
};
|
||||
|
||||
world.movements.with_mut(entity, |movement| {
|
||||
movement.movement_context.is_floored = is_grounded;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl State for LeapingState
|
||||
{
|
||||
fn tick_time(&mut self, delta: f32)
|
||||
{
|
||||
self.time_in_state += delta;
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, world: &mut World, entity: EntityHandle)
|
||||
{
|
||||
self.time_in_state = 0.0;
|
||||
|
||||
let facing = world
|
||||
.transforms
|
||||
.with(entity, |t| {
|
||||
let yaw = t.rotation.y;
|
||||
Vec3::new(yaw.sin(), 0.0, yaw.cos())
|
||||
})
|
||||
.unwrap_or(Vec3::Z);
|
||||
|
||||
let move_dir = world
|
||||
.inputs
|
||||
.with(entity, |i| i.move_direction)
|
||||
.unwrap_or(Vec3::ZERO);
|
||||
|
||||
self.leap_direction = if move_dir.length_squared() > 0.01
|
||||
{
|
||||
move_dir
|
||||
}
|
||||
else
|
||||
{
|
||||
facing
|
||||
};
|
||||
}
|
||||
|
||||
fn on_physics_update(&mut self, world: &mut World, entity: EntityHandle, delta: f32)
|
||||
{
|
||||
let current_translation = world
|
||||
.physics
|
||||
.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rb| *rb.translation())
|
||||
})
|
||||
.flatten()
|
||||
.unwrap();
|
||||
|
||||
let terrain_height =
|
||||
PhysicsManager::get_terrain_height_at(current_translation.x, current_translation.z);
|
||||
let target_y = terrain_height
|
||||
.map(|h| h + 1.0)
|
||||
.unwrap_or(current_translation.y);
|
||||
|
||||
let velocity = self.leap_direction * LEAP_SPEED;
|
||||
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rb| {
|
||||
let next = Vector::new(
|
||||
current_translation.x + velocity.x * delta,
|
||||
target_y,
|
||||
current_translation.z + velocity.z * delta,
|
||||
);
|
||||
rb.set_linvel(Vector::new(velocity.x, 0.0, velocity.z), true);
|
||||
rb.set_next_kinematic_translation(next);
|
||||
});
|
||||
});
|
||||
|
||||
world.movements.with_mut(entity, |m| {
|
||||
m.movement_context.is_floored = terrain_height.is_some();
|
||||
});
|
||||
|
||||
world.transforms.with_mut(entity, |t| {
|
||||
t.rotation.y = f32::atan2(self.leap_direction.x, self.leap_direction.z);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl State for RollingState
|
||||
{
|
||||
fn tick_time(&mut self, delta: f32)
|
||||
{
|
||||
self.time_in_state += delta;
|
||||
}
|
||||
|
||||
fn on_enter(&mut self, world: &mut World, entity: EntityHandle)
|
||||
{
|
||||
self.time_in_state = 0.0;
|
||||
|
||||
self.roll_direction = world
|
||||
.inputs
|
||||
.with(entity, |i| i.move_direction)
|
||||
.filter(|d| d.length_squared() > 0.01)
|
||||
.unwrap_or_else(|| {
|
||||
world
|
||||
.transforms
|
||||
.with(entity, |t| {
|
||||
let yaw = t.rotation.y;
|
||||
Vec3::new(yaw.sin(), 0.0, yaw.cos())
|
||||
})
|
||||
.unwrap_or(Vec3::Z)
|
||||
});
|
||||
}
|
||||
|
||||
fn on_physics_update(&mut self, world: &mut World, entity: EntityHandle, delta: f32)
|
||||
{
|
||||
let t = (self.time_in_state / ROLL_DURATION).clamp(0.0, 1.0);
|
||||
let speed = ROLL_SPEED * (1.0 - t * 0.6);
|
||||
|
||||
let current_translation = world
|
||||
.physics
|
||||
.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rb| *rb.translation())
|
||||
})
|
||||
.flatten()
|
||||
.unwrap();
|
||||
|
||||
let terrain_height =
|
||||
PhysicsManager::get_terrain_height_at(current_translation.x, current_translation.z);
|
||||
let target_y = terrain_height
|
||||
.map(|h| h + 1.0)
|
||||
.unwrap_or(current_translation.y);
|
||||
|
||||
let velocity = self.roll_direction * speed;
|
||||
|
||||
world.physics.with(entity, |physics| {
|
||||
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rb| {
|
||||
let next = Vector::new(
|
||||
current_translation.x + velocity.x * delta,
|
||||
target_y,
|
||||
current_translation.z + velocity.z * delta,
|
||||
);
|
||||
rb.set_linvel(Vector::new(velocity.x, 0.0, velocity.z), true);
|
||||
rb.set_next_kinematic_translation(next);
|
||||
});
|
||||
});
|
||||
|
||||
world.movements.with_mut(entity, |m| {
|
||||
m.movement_context.is_floored = terrain_height.is_some();
|
||||
});
|
||||
}
|
||||
}
|
||||
177
src/states/state.rs
Normal file
177
src/states/state.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use std::any::TypeId;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::entity::EntityHandle;
|
||||
use crate::world::{Storage, World};
|
||||
|
||||
pub trait State
|
||||
{
|
||||
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<dyn Fn(&mut World, EntityHandle)>,
|
||||
insert_default: Box<dyn Fn(&mut World, EntityHandle)>,
|
||||
on_enter: Box<dyn Fn(&mut World, EntityHandle)>,
|
||||
on_exit: Box<dyn Fn(&mut World, EntityHandle)>,
|
||||
tick: Box<dyn Fn(&mut World, EntityHandle, f32)>,
|
||||
physics_tick: Box<dyn Fn(&mut World, EntityHandle, f32)>,
|
||||
}
|
||||
|
||||
struct StateTransition
|
||||
{
|
||||
to_state_id: TypeId,
|
||||
condition: Box<dyn Fn(&World) -> bool>,
|
||||
}
|
||||
|
||||
pub struct StateMachine
|
||||
{
|
||||
state_ops: HashMap<TypeId, StateOps>,
|
||||
state_transitions: HashMap<TypeId, Vec<StateTransition>>,
|
||||
current_state_id: TypeId,
|
||||
}
|
||||
|
||||
fn short_type_name<T: 'static>() -> &'static str
|
||||
{
|
||||
let full = std::any::type_name::<T>();
|
||||
full.rsplit("::").next().unwrap_or(full)
|
||||
}
|
||||
|
||||
impl StateMachine
|
||||
{
|
||||
pub fn new<S: 'static>() -> Self
|
||||
{
|
||||
Self {
|
||||
state_ops: HashMap::new(),
|
||||
state_transitions: HashMap::new(),
|
||||
current_state_id: TypeId::of::<S>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_state<S: State + Default + 'static>(
|
||||
&mut self,
|
||||
storage: fn(&mut World) -> &mut Storage<S>,
|
||||
)
|
||||
{
|
||||
let ops = StateOps {
|
||||
name: short_type_name::<S>(),
|
||||
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::<S>(), 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<TypeId>
|
||||
{
|
||||
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<TFrom: 'static, TTo: 'static>(
|
||||
&mut self,
|
||||
condition: impl Fn(&World) -> bool + 'static,
|
||||
)
|
||||
{
|
||||
let from_id = TypeId::of::<TFrom>();
|
||||
let to_id = TypeId::of::<TTo>();
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user