416 lines
13 KiB
Rust
416 lines
13 KiB
Rust
use glam::Vec3;
|
|
use kurbo::ParamCurve;
|
|
use rapier3d::math::Vector;
|
|
|
|
use crate::entity::EntityHandle;
|
|
use crate::physics::PhysicsManager;
|
|
use crate::state::State;
|
|
use crate::utility::time::Time;
|
|
use crate::world::World;
|
|
|
|
pub struct PlayerFallingState
|
|
{
|
|
pub entity: EntityHandle,
|
|
}
|
|
|
|
impl State for PlayerFallingState
|
|
{
|
|
fn get_state_name(&self) -> &'static str
|
|
{
|
|
"PlayerFallingState"
|
|
}
|
|
|
|
fn on_state_enter(&mut self, _world: &mut World)
|
|
{
|
|
println!("entered falling");
|
|
}
|
|
|
|
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)
|
|
{
|
|
const GRAVITY: f32 = -9.81 * 5.0;
|
|
const GROUND_CHECK_DISTANCE: f32 = 0.6;
|
|
|
|
let (current_pos, velocity) = world
|
|
.physics
|
|
.with(self.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(self.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(self.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(self.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(self.entity, |movement| {
|
|
movement.movement_context.is_floored = is_grounded;
|
|
});
|
|
}
|
|
}
|
|
|
|
pub struct PlayerIdleState
|
|
{
|
|
pub entity: EntityHandle,
|
|
}
|
|
|
|
impl State for PlayerIdleState
|
|
{
|
|
fn get_state_name(&self) -> &'static str
|
|
{
|
|
"PlayerIdleState"
|
|
}
|
|
|
|
fn on_state_enter(&mut self, world: &mut World)
|
|
{
|
|
println!("entered idle");
|
|
|
|
world.physics.with(self.entity, |physics| {
|
|
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
|
let current_velocity = *rigidbody.linvel();
|
|
let idle_damping = world
|
|
.movements
|
|
.with(self.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_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)
|
|
{
|
|
const GROUND_CHECK_DISTANCE: f32 = 0.6;
|
|
|
|
let current_translation = world
|
|
.physics
|
|
.with(self.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(self.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(self.entity, |movement| {
|
|
movement.movement_context.is_floored = true;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct PlayerWalkingState
|
|
{
|
|
pub entity: EntityHandle,
|
|
pub enter_time_stamp: f32,
|
|
}
|
|
|
|
impl State for PlayerWalkingState
|
|
{
|
|
fn get_state_name(&self) -> &'static str
|
|
{
|
|
"PlayerWalkingState"
|
|
}
|
|
|
|
fn on_state_enter(&mut self, _world: &mut World)
|
|
{
|
|
self.enter_time_stamp = Time::get_time_elapsed();
|
|
println!("entered walking");
|
|
}
|
|
|
|
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)
|
|
{
|
|
let (movement_input, walking_config) = world
|
|
.movements
|
|
.with(self.entity, |movement| {
|
|
let input = world
|
|
.inputs
|
|
.with(self.entity, |input| input.move_direction)
|
|
.unwrap_or(Vec3::ZERO);
|
|
(input, movement.clone())
|
|
})
|
|
.unwrap();
|
|
|
|
let current_time = Time::get_time_elapsed();
|
|
let elapsed_time = current_time - self.enter_time_stamp;
|
|
|
|
let t = (elapsed_time / 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(self.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(self.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(self.entity, |movement| {
|
|
movement.movement_context.is_floored = terrain_height.is_some();
|
|
});
|
|
|
|
if movement_input.length_squared() > 0.1
|
|
{
|
|
world.transforms.with_mut(self.entity, |transform| {
|
|
let target_rotation = f32::atan2(movement_input.x, movement_input.z);
|
|
transform.rotation.y = target_rotation;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct PlayerJumpingState
|
|
{
|
|
pub entity: EntityHandle,
|
|
pub enter_time_stamp: f32,
|
|
}
|
|
|
|
impl State for PlayerJumpingState
|
|
{
|
|
fn get_state_name(&self) -> &'static str
|
|
{
|
|
"PlayerJumpingState"
|
|
}
|
|
|
|
fn on_state_enter(&mut self, world: &mut World)
|
|
{
|
|
self.enter_time_stamp = Time::get_time_elapsed();
|
|
|
|
let current_position = world.transforms.get(self.entity).unwrap().position;
|
|
|
|
world.jumps.with_mut(self.entity, |jump| {
|
|
jump.jump_context.in_progress = true;
|
|
jump.jump_context.execution_time = self.enter_time_stamp;
|
|
jump.jump_context.origin_height = current_position.y;
|
|
jump.jump_context.duration = 0.0;
|
|
jump.jump_context.normal = Vec3::Y;
|
|
});
|
|
|
|
println!("entered jumping");
|
|
}
|
|
|
|
fn on_state_exit(&mut self, world: &mut World)
|
|
{
|
|
world.jumps.with_mut(self.entity, |jump| {
|
|
jump.jump_context.in_progress = false;
|
|
jump.jump_context.duration = 0.0;
|
|
});
|
|
|
|
println!("exited jumping");
|
|
}
|
|
|
|
fn on_state_update(&mut self, _world: &mut World, _delta: f32) {}
|
|
|
|
fn on_state_physics_update(&mut self, world: &mut World, delta: f32)
|
|
{
|
|
let current_time = Time::get_time_elapsed();
|
|
|
|
world.jumps.with_mut(self.entity, |jump| {
|
|
jump.jump_context.duration =
|
|
current_time - jump.jump_context.execution_time;
|
|
});
|
|
|
|
let jump = world
|
|
.jumps
|
|
.with(self.entity, |jump| jump.clone())
|
|
.unwrap();
|
|
|
|
let elapsed_time = jump.jump_context.duration;
|
|
let normalized_time = (elapsed_time / 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(self.entity, |physics| {
|
|
PhysicsManager::with_rigidbody_mut(physics.rigidbody, |rigidbody| {
|
|
*rigidbody.translation()
|
|
})
|
|
})
|
|
.flatten()
|
|
.unwrap();
|
|
|
|
let current_y = current_translation.y;
|
|
let height_diff = target_height - current_y;
|
|
let required_velocity = height_diff / delta;
|
|
|
|
world.physics.with(self.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(self.entity, |movement| {
|
|
movement.movement_context.is_floored = false;
|
|
});
|
|
}
|
|
}
|