veloren/common/src/states/climb.rs

114 lines
3.7 KiB
Rust
Raw Normal View History

2020-02-24 18:17:16 +00:00
use crate::{
2020-03-24 07:38:16 +00:00
comp::{CharacterState, Climb, EnergySource, StateUpdate},
2020-03-07 18:15:02 +00:00
event::LocalEvent,
2020-03-14 21:17:27 +00:00
sys::{
character_behavior::{CharacterBehavior, JoinData},
phys::GRAVITY,
},
util::safe_slerp,
2020-02-24 18:17:16 +00:00
};
use vek::{
vec::{Vec2, Vec3},
Lerp,
};
2019-12-26 14:43:59 +00:00
2020-01-07 15:49:08 +00:00
const HUMANOID_CLIMB_ACCEL: f32 = 5.0;
const CLIMB_SPEED: f32 = 5.0;
2020-03-14 21:17:27 +00:00
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Data;
2019-12-26 14:43:59 +00:00
2020-03-14 21:17:27 +00:00
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate::from(data);
2020-02-24 18:17:16 +00:00
2020-03-24 07:38:16 +00:00
// If no wall is in front of character or we stopped climbing;
if data.physics.on_wall.is_none() || data.physics.on_ground || data.inputs.climb.is_none() {
2020-03-14 21:17:27 +00:00
if data.inputs.jump.is_pressed() {
// They've climbed atop something, give them a boost
update
.local_events
.push_front(LocalEvent::Jump(data.entity));
}
update.character = CharacterState::Idle {};
return update;
2019-12-26 14:43:59 +00:00
}
2020-03-14 21:17:27 +00:00
// Move player
update.vel.0 += Vec2::broadcast(data.dt.0)
* data.inputs.move_dir
* if update.vel.0.magnitude_squared() < CLIMB_SPEED.powf(2.0) {
HUMANOID_CLIMB_ACCEL
} else {
0.0
};
2019-12-26 14:43:59 +00:00
2020-03-24 07:38:16 +00:00
// Expend energy if climbing
let energy_use = match data.inputs.climb {
Some(Climb::Up) | Some(Climb::Down) => 8,
Some(Climb::Hold) => data
.physics
.on_wall
.map(|wall_dir| {
// Calculate velocity perpendicular to the wall
let vel = update.vel.0;
let perp_vel =
vel - wall_dir * vel.dot(wall_dir) / wall_dir.magnitude_squared();
// Enegry cost based on magnitude of this velocity
(perp_vel.magnitude() * 8.0) as i32
})
// Note: this is currently unreachable
.unwrap_or(0),
// Note: this is currently unreachable
None => 0,
};
if let Err(_) = update
.energy
.try_change_by(-energy_use, EnergySource::Climb)
{
update.character = CharacterState::Idle {};
}
2020-03-14 21:17:27 +00:00
// Set orientation direction based on wall direction
let ori_dir = if let Some(wall_dir) = data.physics.on_wall {
2020-03-24 07:38:16 +00:00
Vec2::from(wall_dir)
2020-03-07 18:15:02 +00:00
} else {
2020-03-14 21:17:27 +00:00
Vec2::from(update.vel.0)
};
// Smooth orientation
update.ori.0 = safe_slerp(
update.ori.0,
ori_dir.into(),
if data.physics.on_ground { 9.0 } else { 2.0 } * data.dt.0,
);
2020-03-07 18:15:02 +00:00
2020-03-14 21:17:27 +00:00
// Apply Vertical Climbing Movement
2020-03-24 07:38:16 +00:00
if let (Some(climb), true, Some(_wall_dir)) = (
data.inputs.climb,
update.vel.0.z <= CLIMB_SPEED,
2020-03-14 21:17:27 +00:00
data.physics.on_wall,
) {
2020-03-24 07:38:16 +00:00
match climb {
Climb::Down => {
update.vel.0 -=
data.dt.0 * update.vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0);
},
Climb::Up => {
update.vel.0.z = (update.vel.0.z + data.dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED);
},
Climb::Hold => {
update.vel.0.z = update.vel.0.z + data.dt.0 * GRAVITY * 1.5;
update.vel.0 = Lerp::lerp(
update.vel.0,
Vec3::zero(),
30.0 * data.dt.0 / (1.0 - update.vel.0.z.min(0.0) * 5.0),
);
},
2020-03-14 21:17:27 +00:00
}
}
update
}
2019-12-26 14:43:59 +00:00
}