263 lines
9.3 KiB
Rust
263 lines
9.3 KiB
Rust
use std::f32::consts::PI;
|
|
use std::rc::Rc;
|
|
|
|
use glam::Vec3;
|
|
use rapier3d::control::{CharacterAutostep, KinematicCharacterController};
|
|
use rapier3d::prelude::{ColliderBuilder, RigidBodyBuilder};
|
|
|
|
use crate::bundles::Bundle;
|
|
use crate::components::lights::spot::SpotlightComponent;
|
|
use crate::components::player_states::{
|
|
FallingState, IdleState, JumpingState, LeapingState, RollingState, WalkingState,
|
|
};
|
|
use crate::components::{
|
|
InputComponent, JumpComponent, MeshComponent, MovementComponent, PhysicsComponent,
|
|
};
|
|
use crate::entity::EntityHandle;
|
|
use crate::loaders::mesh::Mesh;
|
|
use crate::paths;
|
|
use crate::physics::PhysicsManager;
|
|
use crate::render::Pipeline;
|
|
use crate::state::StateMachine;
|
|
use crate::systems::player_states::{LEAP_DURATION, ROLL_DURATION};
|
|
use crate::world::{Transform, World};
|
|
|
|
pub struct PlayerBundle
|
|
{
|
|
pub position: Vec3,
|
|
}
|
|
|
|
impl Bundle for PlayerBundle
|
|
{
|
|
fn spawn(self, world: &mut World) -> Result<EntityHandle, String>
|
|
{
|
|
let entity = world.spawn();
|
|
|
|
let spawn_transform = Transform::from_position(self.position);
|
|
|
|
let rigidbody = RigidBodyBuilder::kinematic_position_based()
|
|
.translation(spawn_transform.position.into())
|
|
.build();
|
|
let collider = ColliderBuilder::capsule_y(0.5, 0.5).build();
|
|
let _controller = KinematicCharacterController {
|
|
slide: true,
|
|
autostep: Some(CharacterAutostep::default()),
|
|
max_slope_climb_angle: 45.0,
|
|
..Default::default()
|
|
};
|
|
|
|
let rigidbody_handle = PhysicsManager::add_rigidbody(rigidbody);
|
|
let collider_handle = PhysicsManager::add_collider(collider, Some(rigidbody_handle));
|
|
|
|
let mesh = Mesh::load_mesh(&paths::meshes::player())
|
|
.map_err(|e| format!("missing player mesh: {}", e))?;
|
|
|
|
let mut state_machine = StateMachine::new::<FallingState>();
|
|
state_machine.register_state(|w: &mut World| &mut w.falling_states);
|
|
state_machine.register_state(|w: &mut World| &mut w.idle_states);
|
|
state_machine.register_state(|w: &mut World| &mut w.walking_states);
|
|
state_machine.register_state(|w: &mut World| &mut w.jumping_states);
|
|
state_machine.register_state(|w: &mut World| &mut w.leaping_states);
|
|
state_machine.register_state(|w: &mut World| &mut w.rolling_states);
|
|
|
|
let entity_id = entity;
|
|
|
|
state_machine.add_transition::<FallingState, IdleState>(move |world| {
|
|
let is_grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
let has_input = world
|
|
.inputs
|
|
.with(entity_id, |i| i.move_direction.length() > 0.01)
|
|
.unwrap_or(false);
|
|
is_grounded && !has_input
|
|
});
|
|
|
|
state_machine.add_transition::<FallingState, WalkingState>(move |world| {
|
|
let is_grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
let has_input = world
|
|
.inputs
|
|
.with(entity_id, |i| i.move_direction.length() > 0.01)
|
|
.unwrap_or(false);
|
|
is_grounded && has_input
|
|
});
|
|
|
|
state_machine.add_transition::<IdleState, WalkingState>(move |world| {
|
|
let is_grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
let has_input = world
|
|
.inputs
|
|
.with(entity_id, |i| i.move_direction.length() > 0.01)
|
|
.unwrap_or(false);
|
|
is_grounded && has_input
|
|
});
|
|
|
|
state_machine.add_transition::<WalkingState, IdleState>(move |world| {
|
|
let is_grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
let has_input = world
|
|
.inputs
|
|
.with(entity_id, |i| i.move_direction.length() > 0.01)
|
|
.unwrap_or(false);
|
|
is_grounded && !has_input
|
|
});
|
|
|
|
state_machine.add_transition::<IdleState, FallingState>(move |world| {
|
|
!world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false)
|
|
});
|
|
|
|
state_machine.add_transition::<WalkingState, FallingState>(move |world| {
|
|
!world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false)
|
|
});
|
|
|
|
state_machine.add_transition::<IdleState, JumpingState>(move |world| {
|
|
let is_grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
let jump_pressed = world
|
|
.inputs
|
|
.with(entity_id, |i| i.jump_just_pressed)
|
|
.unwrap_or(false);
|
|
is_grounded && jump_pressed
|
|
});
|
|
|
|
state_machine.add_transition::<WalkingState, JumpingState>(move |world| {
|
|
let is_grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
let jump_pressed = world
|
|
.inputs
|
|
.with(entity_id, |i| i.jump_just_pressed)
|
|
.unwrap_or(false);
|
|
is_grounded && jump_pressed
|
|
});
|
|
|
|
state_machine.add_transition::<JumpingState, FallingState>(move |world| {
|
|
let time = world
|
|
.jumping_states
|
|
.with(entity_id, |s| s.time_in_state)
|
|
.unwrap_or(0.0);
|
|
world
|
|
.jumps
|
|
.with(entity_id, |j| time >= j.jump_duration)
|
|
.unwrap_or(true)
|
|
});
|
|
|
|
state_machine.add_transition::<IdleState, LeapingState>(move |world| {
|
|
world
|
|
.inputs
|
|
.with(entity_id, |i| i.roll_just_pressed)
|
|
.unwrap_or(false)
|
|
});
|
|
|
|
state_machine.add_transition::<WalkingState, LeapingState>(move |world| {
|
|
world
|
|
.inputs
|
|
.with(entity_id, |i| i.roll_just_pressed)
|
|
.unwrap_or(false)
|
|
});
|
|
|
|
state_machine.add_transition::<LeapingState, RollingState>(move |world| {
|
|
world
|
|
.leaping_states
|
|
.with(entity_id, |s| s.time_in_state >= LEAP_DURATION)
|
|
.unwrap_or(false)
|
|
});
|
|
|
|
state_machine.add_transition::<LeapingState, FallingState>(move |world| {
|
|
let leap_done = world
|
|
.leaping_states
|
|
.with(entity_id, |s| s.time_in_state >= LEAP_DURATION)
|
|
.unwrap_or(false);
|
|
let not_grounded = !world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(true);
|
|
leap_done && not_grounded
|
|
});
|
|
|
|
state_machine.add_transition::<RollingState, IdleState>(move |world| {
|
|
let done = world
|
|
.rolling_states
|
|
.with(entity_id, |s| s.time_in_state >= ROLL_DURATION)
|
|
.unwrap_or(false);
|
|
let grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
done && grounded
|
|
});
|
|
|
|
state_machine.add_transition::<RollingState, FallingState>(move |world| {
|
|
let done = world
|
|
.rolling_states
|
|
.with(entity_id, |s| s.time_in_state >= ROLL_DURATION)
|
|
.unwrap_or(false);
|
|
let grounded = world
|
|
.movements
|
|
.with(entity_id, |m| m.movement_context.is_floored)
|
|
.unwrap_or(false);
|
|
done && !grounded
|
|
});
|
|
|
|
world.falling_states.insert(entity, FallingState::default());
|
|
|
|
world.transforms.insert(entity, spawn_transform);
|
|
world.movements.insert(entity, MovementComponent::new());
|
|
world.jumps.insert(entity, JumpComponent::default());
|
|
world.inputs.insert(entity, InputComponent::default());
|
|
world.physics.insert(
|
|
entity,
|
|
PhysicsComponent {
|
|
rigidbody: rigidbody_handle,
|
|
collider: Some(collider_handle),
|
|
},
|
|
);
|
|
world.meshes.insert(
|
|
entity,
|
|
MeshComponent {
|
|
mesh: Rc::new(mesh),
|
|
pipeline: Pipeline::Standard,
|
|
instance_buffer: None,
|
|
num_instances: 1,
|
|
tile_scale: 4.0,
|
|
enable_dissolve: false,
|
|
enable_snow_light: false,
|
|
},
|
|
);
|
|
world.player_tags.insert(entity, ());
|
|
world.state_machines.insert(entity, state_machine);
|
|
world.names.insert(entity, "Player".to_string());
|
|
|
|
let outer_angle = PI / 2.0 * 0.9;
|
|
world.spotlights.insert(
|
|
entity,
|
|
SpotlightComponent::new(
|
|
Vec3::new(1.0, 2.5, 1.0),
|
|
Vec3::new(0.0, -1.0, 0.0),
|
|
100.0,
|
|
outer_angle * 0.5,
|
|
outer_angle,
|
|
),
|
|
);
|
|
|
|
Ok(entity)
|
|
}
|
|
}
|