2020-02-03 10:54:50 +00:00
|
|
|
use crate::{
|
2020-03-07 18:15:02 +00:00
|
|
|
comp::{AbilityState, CharacterState, EnergySource, ItemKind::Tool, StateUpdate},
|
2020-02-03 10:54:50 +00:00
|
|
|
event::LocalEvent,
|
2020-03-07 19:02:54 +00:00
|
|
|
sys::{character_behavior::JoinData, phys::GRAVITY},
|
2019-12-28 16:10:39 +00:00
|
|
|
};
|
2020-02-11 15:42:17 +00:00
|
|
|
use std::time::Duration;
|
2020-01-21 22:54:32 +00:00
|
|
|
use vek::vec::{Vec2, Vec3};
|
2019-12-28 16:10:39 +00:00
|
|
|
|
2020-03-08 15:38:53 +00:00
|
|
|
const BASE_HUMANOID_ACCEL: f32 = 100.0;
|
|
|
|
const BASE_HUMANOID_SPEED: f32 = 150.0;
|
|
|
|
const BASE_HUMANOID_AIR_ACCEL: f32 = 15.0;
|
|
|
|
const BASE_HUMANOID_AIR_SPEED: f32 = 8.0;
|
|
|
|
const BASE_HUMANOID_WATER_ACCEL: f32 = 70.0;
|
|
|
|
const BASE_HUMANOID_WATER_SPEED: f32 = 120.0;
|
|
|
|
// const BASE_HUMANOID_CLIMB_ACCEL: f32 = 10.0;
|
|
|
|
// const ROLL_SPEED: f32 = 17.0;
|
|
|
|
// const CHARGE_SPEED: f32 = 20.0;
|
|
|
|
// const GLIDE_ACCEL: f32 = 15.0;
|
|
|
|
// const GLIDE_SPEED: f32 = 45.0;
|
|
|
|
// const BLOCK_ACCEL: f32 = 30.0;
|
|
|
|
// const BLOCK_SPEED: f32 = 75.0;
|
|
|
|
// // Gravity is 9.81 * 4, so this makes gravity equal to .15
|
|
|
|
// const GLIDE_ANTIGRAV: f32 = GRAVITY * 0.96;
|
|
|
|
// const CLIMB_SPEED: f32 = 5.0;
|
|
|
|
// const CLIMB_COST: i32 = 5;
|
|
|
|
|
|
|
|
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
|
2020-03-07 18:15:02 +00:00
|
|
|
|
|
|
|
pub fn handle_move(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if data.physics.in_fluid {
|
2020-03-07 23:45:10 +00:00
|
|
|
swim_move(data, update);
|
2020-03-07 18:15:02 +00:00
|
|
|
} else {
|
2020-03-07 23:45:10 +00:00
|
|
|
ground_move(data, update);
|
2020-03-07 18:15:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 23:45:10 +00:00
|
|
|
fn ground_move(data: &JoinData, update: &mut StateUpdate) {
|
2020-03-07 18:15:02 +00:00
|
|
|
let (accel, speed): (f32, f32) = if data.physics.on_ground {
|
2020-03-08 15:38:53 +00:00
|
|
|
(BASE_HUMANOID_ACCEL, BASE_HUMANOID_SPEED)
|
2020-01-21 22:54:32 +00:00
|
|
|
} else {
|
2020-03-08 15:38:53 +00:00
|
|
|
(BASE_HUMANOID_AIR_ACCEL, BASE_HUMANOID_AIR_SPEED)
|
2020-01-21 22:54:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Move player according to move_dir
|
2020-02-03 19:43:36 +00:00
|
|
|
if update.vel.0.magnitude_squared() < speed.powf(2.0) {
|
2020-03-07 18:15:02 +00:00
|
|
|
update.vel.0 = update.vel.0 + Vec2::broadcast(data.dt.0) * data.inputs.move_dir * accel;
|
2020-02-03 19:43:36 +00:00
|
|
|
let mag2 = update.vel.0.magnitude_squared();
|
|
|
|
if mag2 > speed.powf(2.0) {
|
|
|
|
update.vel.0 = update.vel.0.normalized() * speed;
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:54:32 +00:00
|
|
|
|
|
|
|
// Set direction based on move direction
|
2020-03-07 20:49:48 +00:00
|
|
|
let ori_dir = if update.character.is_wield()
|
2020-02-24 19:57:33 +00:00
|
|
|
|| update.character.is_attack()
|
|
|
|
|| update.character.is_block()
|
|
|
|
{
|
2020-03-07 18:15:02 +00:00
|
|
|
Vec2::from(data.inputs.look_dir).normalized()
|
2020-01-21 22:54:32 +00:00
|
|
|
} else {
|
|
|
|
Vec2::from(update.vel.0)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Smooth orientation
|
|
|
|
if ori_dir.magnitude_squared() > 0.0001
|
|
|
|
&& (update.ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared()
|
|
|
|
> 0.001
|
|
|
|
{
|
2020-03-07 18:15:02 +00:00
|
|
|
update.ori.0 = vek::ops::Slerp::slerp(update.ori.0, ori_dir.into(), 9.0 * data.dt.0);
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 23:45:10 +00:00
|
|
|
fn swim_move(data: &JoinData, update: &mut StateUpdate) {
|
2020-03-07 18:15:02 +00:00
|
|
|
// Update velocity
|
|
|
|
update.vel.0 += Vec2::broadcast(data.dt.0)
|
|
|
|
* data.inputs.move_dir
|
2020-03-08 15:38:53 +00:00
|
|
|
* if update.vel.0.magnitude_squared() < BASE_HUMANOID_WATER_SPEED.powf(2.0) {
|
|
|
|
BASE_HUMANOID_WATER_ACCEL
|
2020-03-07 18:15:02 +00:00
|
|
|
} else {
|
|
|
|
0.0
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set direction based on move direction when on the ground
|
|
|
|
let ori_dir = if update.character.is_attack() || update.character.is_block() {
|
|
|
|
Vec2::from(data.inputs.look_dir).normalized()
|
|
|
|
} else {
|
|
|
|
Vec2::from(update.vel.0)
|
|
|
|
};
|
|
|
|
|
|
|
|
if ori_dir.magnitude_squared() > 0.0001
|
|
|
|
&& (update.ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared()
|
|
|
|
> 0.001
|
|
|
|
{
|
|
|
|
update.ori.0 = vek::ops::Slerp::slerp(
|
|
|
|
update.ori.0,
|
|
|
|
ori_dir.into(),
|
|
|
|
if data.physics.on_ground { 9.0 } else { 2.0 } * data.dt.0,
|
|
|
|
);
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
// Force players to pulse jump button to swim up
|
|
|
|
if data.inputs.jump.is_pressed() && !data.inputs.jump.is_long_press(Duration::from_millis(600))
|
2020-01-21 22:54:32 +00:00
|
|
|
{
|
2020-03-08 15:38:53 +00:00
|
|
|
update.vel.0.z =
|
|
|
|
(update.vel.0.z + data.dt.0 * GRAVITY * 1.25).min(BASE_HUMANOID_WATER_SPEED);
|
2020-03-07 18:15:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 17:04:26 +00:00
|
|
|
/// First checks whether `primary` input is pressed, then
|
|
|
|
/// attempts to go into Equipping state, otherwise Idle
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if data.inputs.primary.is_pressed() {
|
2020-03-08 17:04:26 +00:00
|
|
|
attempt_wield(data, update);
|
2020-03-07 18:15:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 17:04:26 +00:00
|
|
|
/// If a tool is equipped, goes into Equipping state, otherwise goes to Idle
|
|
|
|
pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if let Some(Tool(tool)) = data.stats.equipment.main.as_ref().map(|i| i.kind) {
|
|
|
|
update.character = CharacterState::Equipping {
|
|
|
|
tool,
|
|
|
|
time_left: tool.equip_time(),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
update.character = CharacterState::Idle {};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_sit(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if data.inputs.sit.is_pressed() && data.physics.on_ground && data.body.is_humanoid() {
|
|
|
|
update.character = CharacterState::Sit {};
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if (data.inputs.climb.is_pressed() || data.inputs.climb_down.is_pressed())
|
|
|
|
&& data.physics.on_wall.is_some()
|
|
|
|
&& !data.physics.on_ground
|
2020-02-03 19:43:36 +00:00
|
|
|
//&& update.vel.0.z < 0.0
|
2020-03-07 18:15:02 +00:00
|
|
|
&& data.body.is_humanoid()
|
2020-01-21 22:54:32 +00:00
|
|
|
{
|
2020-03-07 18:15:02 +00:00
|
|
|
update.character = CharacterState::Climb {};
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_unwield(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if let CharacterState::Wielding { .. } = update.character {
|
|
|
|
if data.inputs.toggle_wield.is_pressed() {
|
|
|
|
update.character = CharacterState::Idle {};
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_glide(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if let CharacterState::Idle { .. } | CharacterState::Wielding { .. } = update.character {
|
|
|
|
if data.inputs.glide.is_pressed() && !data.physics.on_ground && data.body.is_humanoid() {
|
|
|
|
update.character = CharacterState::Glide {};
|
2020-02-03 10:54:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if data.inputs.jump.is_pressed() && data.physics.on_ground {
|
2020-02-03 10:54:50 +00:00
|
|
|
update
|
|
|
|
.local_events
|
2020-03-07 18:15:02 +00:00
|
|
|
.push_front(LocalEvent::Jump(data.entity));
|
2020-02-03 10:54:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_primary(data: &JoinData, update: &mut StateUpdate) {
|
2020-03-08 15:38:53 +00:00
|
|
|
if let Some(ability_state) = data.ability_pool.primary {
|
2020-03-07 18:15:02 +00:00
|
|
|
if let CharacterState::Wielding { .. } = update.character {
|
|
|
|
if data.inputs.primary.is_pressed() {
|
|
|
|
// data.updater.insert(data.entity, state);
|
2020-03-08 15:38:53 +00:00
|
|
|
update.character = character_state_from_ability(data, ability_state);
|
2020-02-03 10:54:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_secondary(data: &JoinData, update: &mut StateUpdate) {
|
2020-03-08 15:38:53 +00:00
|
|
|
if let Some(ability_state) = data.ability_pool.secondary {
|
2020-03-07 18:15:02 +00:00
|
|
|
if let CharacterState::Wielding { .. } = update.character {
|
|
|
|
if data.inputs.secondary.is_pressed() {
|
|
|
|
// data.updater.insert(data.entity, state);
|
2020-03-08 15:38:53 +00:00
|
|
|
update.character = character_state_from_ability(data, ability_state);
|
2020-02-24 14:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_dodge(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if let Some(state) = data.ability_pool.dodge {
|
|
|
|
if let CharacterState::Idle { .. } | CharacterState::Wielding { .. } = update.character {
|
|
|
|
if data.inputs.roll.is_pressed()
|
|
|
|
&& data.physics.on_ground
|
|
|
|
&& data.body.is_humanoid()
|
2020-02-24 19:57:33 +00:00
|
|
|
&& update
|
|
|
|
.energy
|
|
|
|
.try_change_by(-200, EnergySource::Roll)
|
|
|
|
.is_ok()
|
2020-02-03 10:54:50 +00:00
|
|
|
{
|
2020-03-07 18:15:02 +00:00
|
|
|
// let tool_data =
|
|
|
|
// if let Some(Tool(data)) = data.stats.equipment.main.as_ref().map(|i|
|
|
|
|
// i.kind) { data
|
|
|
|
// } else {
|
|
|
|
// ToolData::default()
|
|
|
|
// };
|
|
|
|
update.character = CharacterState::Roll {
|
|
|
|
remaining_duration: Duration::from_millis(600), // tool_data.attack_duration(),
|
|
|
|
};
|
|
|
|
data.updater.insert(data.entity, state);
|
2020-02-03 10:54:50 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
2019-12-28 16:10:39 +00:00
|
|
|
}
|
2020-03-07 18:15:02 +00:00
|
|
|
|
|
|
|
pub fn character_state_from_ability(
|
|
|
|
data: &JoinData,
|
|
|
|
ability_state: AbilityState,
|
|
|
|
) -> CharacterState {
|
|
|
|
match ability_state {
|
|
|
|
AbilityState::BasicAttack { .. } => {
|
|
|
|
if let Some(Tool(tool)) = data.stats.equipment.main.as_ref().map(|i| i.kind) {
|
|
|
|
CharacterState::BasicAttack {
|
|
|
|
exhausted: false,
|
|
|
|
remaining_duration: tool.attack_duration(),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CharacterState::Idle {}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
AbilityState::BasicBlock { .. } => CharacterState::BasicBlock {},
|
|
|
|
AbilityState::Roll { .. } => CharacterState::Roll {
|
|
|
|
remaining_duration: Duration::from_millis(600),
|
|
|
|
},
|
2020-03-08 17:04:26 +00:00
|
|
|
AbilityState::ChargeAttack { .. } => CharacterState::ChargeAttack {
|
|
|
|
remaining_duration: Duration::from_millis(600),
|
|
|
|
},
|
2020-03-07 18:15:02 +00:00
|
|
|
}
|
|
|
|
}
|