2020-02-03 10:54:50 +00:00
|
|
|
use crate::{
|
2020-03-28 05:51:52 +00:00
|
|
|
comp::{
|
2021-02-20 21:56:10 +00:00
|
|
|
biped_large, biped_small,
|
2021-01-08 19:12:09 +00:00
|
|
|
inventory::slot::EquipSlot,
|
2021-02-14 06:01:53 +00:00
|
|
|
item::{Hands, ItemKind, Tool, ToolKind},
|
2021-03-12 22:14:08 +00:00
|
|
|
quadruped_low, quadruped_medium, quadruped_small, ship,
|
2021-02-19 23:45:48 +00:00
|
|
|
skills::Skill,
|
2021-03-11 16:48:59 +00:00
|
|
|
theropod, ship, Body, CharacterAbility, CharacterState, InputKind, InventoryAction, StateUpdate,
|
2020-03-28 05:51:52 +00:00
|
|
|
},
|
2020-12-01 00:28:00 +00:00
|
|
|
consts::{FRIC_GROUND, GRAVITY},
|
2021-02-07 17:15:29 +00:00
|
|
|
event::{LocalEvent, ServerEvent},
|
2020-12-01 00:28:00 +00:00
|
|
|
states::{behavior::JoinData, *},
|
2020-03-28 01:31:22 +00:00
|
|
|
util::Dir,
|
2019-12-28 16:10:39 +00:00
|
|
|
};
|
2020-09-10 02:58:28 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2020-10-17 02:29:14 +00:00
|
|
|
use std::time::Duration;
|
2020-08-11 11:13:18 +00:00
|
|
|
use vek::*;
|
2019-12-28 16:10:39 +00:00
|
|
|
|
2020-03-08 19:37:17 +00:00
|
|
|
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
|
2021-03-13 15:36:56 +00:00
|
|
|
const BASE_HUMANOID_AIR_ACCEL: f32 = 2.0;
|
|
|
|
const BASE_FLIGHT_ACCEL: f32 = 2.0;
|
2020-04-26 01:09:03 +00:00
|
|
|
const BASE_HUMANOID_WATER_ACCEL: f32 = 150.0;
|
|
|
|
const BASE_HUMANOID_WATER_SPEED: f32 = 180.0;
|
2020-03-08 15:38:53 +00:00
|
|
|
// 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;
|
2020-03-10 18:12:16 +00:00
|
|
|
// Gravity is 9.81 * 4, so this makes gravity equal to .15 //TODO: <- is wrong
|
|
|
|
//
|
2020-03-08 15:38:53 +00:00
|
|
|
// const GLIDE_ANTIGRAV: f32 = GRAVITY * 0.96;
|
|
|
|
// const CLIMB_SPEED: f32 = 5.0;
|
|
|
|
// const CLIMB_COST: i32 = 5;
|
|
|
|
|
2020-07-03 18:30:41 +00:00
|
|
|
impl Body {
|
|
|
|
pub fn base_accel(&self) -> f32 {
|
|
|
|
match self {
|
|
|
|
Body::Humanoid(_) => 100.0,
|
2021-02-23 15:19:57 +00:00
|
|
|
Body::QuadrupedSmall(body) => match body.species {
|
|
|
|
quadruped_small::Species::Turtle => 30.0,
|
|
|
|
quadruped_small::Species::Axolotl => 70.0,
|
|
|
|
quadruped_small::Species::Pig => 70.0,
|
|
|
|
quadruped_small::Species::Sheep => 70.0,
|
|
|
|
quadruped_small::Species::Cat => 70.0,
|
|
|
|
quadruped_small::Species::Truffler => 70.0,
|
|
|
|
quadruped_small::Species::Fungome => 70.0,
|
2021-03-12 00:20:05 +00:00
|
|
|
quadruped_small::Species::Goat => 80.0,
|
2021-02-23 15:19:57 +00:00
|
|
|
_ => 125.0,
|
|
|
|
},
|
2020-11-17 07:44:30 +00:00
|
|
|
Body::QuadrupedMedium(quadruped_medium) => match quadruped_medium.species {
|
|
|
|
quadruped_medium::Species::Grolgar => 110.0,
|
|
|
|
quadruped_medium::Species::Saber => 180.0,
|
|
|
|
quadruped_medium::Species::Tiger => 150.0,
|
|
|
|
quadruped_medium::Species::Tuskram => 160.0,
|
2020-11-22 08:09:43 +00:00
|
|
|
quadruped_medium::Species::Lion => 170.0,
|
2020-11-17 07:44:30 +00:00
|
|
|
quadruped_medium::Species::Tarasque => 100.0,
|
|
|
|
quadruped_medium::Species::Wolf => 180.0,
|
|
|
|
quadruped_medium::Species::Frostfang => 180.0,
|
|
|
|
quadruped_medium::Species::Mouflon => 100.0,
|
|
|
|
quadruped_medium::Species::Catoblepas => 70.0,
|
|
|
|
quadruped_medium::Species::Bonerattler => 130.0,
|
|
|
|
quadruped_medium::Species::Deer => 150.0,
|
|
|
|
quadruped_medium::Species::Hirdrasil => 160.0,
|
2020-11-20 06:11:13 +00:00
|
|
|
quadruped_medium::Species::Roshwalr => 160.0,
|
2020-11-17 07:44:30 +00:00
|
|
|
quadruped_medium::Species::Donkey => 110.0,
|
2020-11-20 06:11:13 +00:00
|
|
|
quadruped_medium::Species::Camel => 75.0,
|
|
|
|
quadruped_medium::Species::Zebra => 150.0,
|
|
|
|
quadruped_medium::Species::Antelope => 185.0,
|
|
|
|
quadruped_medium::Species::Kelpie => 180.0,
|
|
|
|
quadruped_medium::Species::Horse => 180.0,
|
2021-02-13 01:01:07 +00:00
|
|
|
quadruped_medium::Species::Barghest => 80.0,
|
|
|
|
quadruped_medium::Species::Cattle => 80.0,
|
|
|
|
quadruped_medium::Species::Darkhound => 160.0,
|
|
|
|
quadruped_medium::Species::Highland => 80.0,
|
|
|
|
quadruped_medium::Species::Yak => 90.0,
|
|
|
|
quadruped_medium::Species::Panda => 90.0,
|
|
|
|
quadruped_medium::Species::Bear => 90.0,
|
2021-03-12 00:20:05 +00:00
|
|
|
quadruped_medium::Species::Dreadhorn => 140.0,
|
|
|
|
quadruped_medium::Species::Moose => 130.0,
|
|
|
|
quadruped_medium::Species::Snowleopard => 160.0,
|
2020-11-17 07:44:30 +00:00
|
|
|
},
|
2021-02-20 20:38:27 +00:00
|
|
|
Body::BipedLarge(body) => match body.species {
|
|
|
|
biped_large::Species::Slysaurok => 100.0,
|
|
|
|
biped_large::Species::Occultsaurok => 100.0,
|
|
|
|
biped_large::Species::Mightysaurok => 100.0,
|
|
|
|
biped_large::Species::Mindflayer => 90.0,
|
|
|
|
biped_large::Species::Minotaur => 90.0,
|
|
|
|
_ => 80.0,
|
2021-02-20 21:56:10 +00:00
|
|
|
},
|
|
|
|
Body::BirdMedium(_) => 80.0,
|
2020-11-26 21:57:38 +00:00
|
|
|
Body::FishMedium(_) => 80.0,
|
2020-07-03 18:30:41 +00:00
|
|
|
Body::Dragon(_) => 250.0,
|
|
|
|
Body::BirdSmall(_) => 75.0,
|
2020-11-26 21:57:38 +00:00
|
|
|
Body::FishSmall(_) => 60.0,
|
2021-02-20 20:38:27 +00:00
|
|
|
Body::BipedSmall(biped_small) => match biped_small.species {
|
|
|
|
biped_small::Species::Haniwa => 65.0,
|
|
|
|
_ => 80.0,
|
2021-02-26 06:19:46 +00:00
|
|
|
},
|
2021-01-18 21:15:18 +00:00
|
|
|
Body::Object(_) => 0.0,
|
2020-07-30 06:13:58 +00:00
|
|
|
Body::Golem(_) => 60.0,
|
2020-08-30 07:40:40 +00:00
|
|
|
Body::Theropod(_) => 135.0,
|
2020-11-17 07:44:30 +00:00
|
|
|
Body::QuadrupedLow(quadruped_low) => match quadruped_low.species {
|
|
|
|
quadruped_low::Species::Crocodile => 130.0,
|
|
|
|
quadruped_low::Species::Alligator => 110.0,
|
|
|
|
quadruped_low::Species::Salamander => 85.0,
|
|
|
|
quadruped_low::Species::Monitor => 160.0,
|
|
|
|
quadruped_low::Species::Asp => 130.0,
|
|
|
|
quadruped_low::Species::Tortoise => 60.0,
|
|
|
|
quadruped_low::Species::Rocksnapper => 70.0,
|
|
|
|
quadruped_low::Species::Pangolin => 120.0,
|
|
|
|
quadruped_low::Species::Maneater => 80.0,
|
2020-11-20 06:11:13 +00:00
|
|
|
quadruped_low::Species::Sandshark => 160.0,
|
|
|
|
quadruped_low::Species::Hakulaq => 140.0,
|
2020-11-21 19:06:36 +00:00
|
|
|
quadruped_low::Species::Lavadrake => 100.0,
|
2021-01-13 23:45:56 +00:00
|
|
|
quadruped_low::Species::Basilisk => 120.0,
|
2021-02-18 00:41:34 +00:00
|
|
|
quadruped_low::Species::Deadwood => 140.0,
|
2020-11-17 07:44:30 +00:00
|
|
|
},
|
2021-03-11 16:48:59 +00:00
|
|
|
Body::Ship(_) => 30.0,
|
2020-07-03 18:30:41 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-07 00:01:39 +00:00
|
|
|
|
2020-11-12 21:31:28 +00:00
|
|
|
/// Attempt to determine the maximum speed of the character
|
|
|
|
/// when moving on the ground
|
|
|
|
pub fn max_speed_approx(&self) -> f32 {
|
|
|
|
// Inverse kinematics: at what velocity will acceleration
|
|
|
|
// be cancelled out by friction drag?
|
|
|
|
// Note: we assume no air (this is fine, current physics
|
|
|
|
// uses max(air_drag, ground_drag)).
|
|
|
|
// Derived via...
|
2020-11-25 00:28:24 +00:00
|
|
|
// v = (v + dv / 30) * (1 - drag).powi(2) (accel cancels drag)
|
|
|
|
// => 1 = (1 + (dv / 30) / v) * (1 - drag).powi(2)
|
|
|
|
// => 1 / (1 - drag).powi(2) = 1 + (dv / 30) / v
|
|
|
|
// => 1 / (1 - drag).powi(2) - 1 = (dv / 30) / v
|
|
|
|
// => 1 / (1 / (1 - drag).powi(2) - 1) = v / (dv / 30)
|
|
|
|
// => (dv / 30) / (1 / (1 - drag).powi(2) - 1) = v
|
|
|
|
let v = (-self.base_accel() / 30.0) / ((1.0 - FRIC_GROUND).powi(2) - 1.0);
|
2020-11-12 21:31:28 +00:00
|
|
|
debug_assert!(v >= 0.0, "Speed must be positive!");
|
|
|
|
v
|
|
|
|
}
|
|
|
|
|
2020-07-07 00:01:39 +00:00
|
|
|
pub fn base_ori_rate(&self) -> f32 {
|
|
|
|
match self {
|
|
|
|
Body::Humanoid(_) => 20.0,
|
|
|
|
Body::QuadrupedSmall(_) => 15.0,
|
2020-11-22 08:09:43 +00:00
|
|
|
Body::QuadrupedMedium(_) => 8.0,
|
2020-07-07 00:01:39 +00:00
|
|
|
Body::BirdMedium(_) => 30.0,
|
|
|
|
Body::FishMedium(_) => 5.0,
|
|
|
|
Body::Dragon(_) => 5.0,
|
|
|
|
Body::BirdSmall(_) => 35.0,
|
|
|
|
Body::FishSmall(_) => 10.0,
|
2021-02-22 06:04:45 +00:00
|
|
|
Body::BipedLarge(_) => 8.0,
|
2020-12-23 06:24:44 +00:00
|
|
|
Body::BipedSmall(_) => 12.0,
|
2021-01-30 14:14:25 +00:00
|
|
|
Body::Object(_) => 10.0,
|
2020-07-07 00:01:39 +00:00
|
|
|
Body::Golem(_) => 8.0,
|
2020-11-21 20:59:25 +00:00
|
|
|
Body::Theropod(theropod) => match theropod.species {
|
2020-11-22 08:09:43 +00:00
|
|
|
theropod::Species::Archaeos => 2.5,
|
|
|
|
theropod::Species::Odonto => 2.5,
|
2021-03-12 00:20:05 +00:00
|
|
|
theropod::Species::Ntouka => 2.5,
|
2020-11-22 08:09:43 +00:00
|
|
|
_ => 7.0,
|
2020-11-21 20:59:25 +00:00
|
|
|
},
|
|
|
|
Body::QuadrupedLow(quadruped_low) => match quadruped_low.species {
|
|
|
|
quadruped_low::Species::Monitor => 9.0,
|
|
|
|
quadruped_low::Species::Asp => 8.0,
|
|
|
|
quadruped_low::Species::Tortoise => 3.0,
|
|
|
|
quadruped_low::Species::Rocksnapper => 4.0,
|
|
|
|
quadruped_low::Species::Maneater => 5.0,
|
2020-11-22 08:09:43 +00:00
|
|
|
quadruped_low::Species::Lavadrake => 4.0,
|
2020-11-21 20:59:25 +00:00
|
|
|
_ => 6.0,
|
|
|
|
},
|
2021-03-12 18:53:01 +00:00
|
|
|
Body::Ship(_) => 0.5,
|
2020-07-07 00:01:39 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-03 22:46:07 +00:00
|
|
|
|
2021-03-12 14:56:47 +00:00
|
|
|
pub fn can_fly(&self) -> Option<f32> {
|
|
|
|
match self {
|
|
|
|
Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_) => Some(1.0),
|
2021-03-12 17:08:40 +00:00
|
|
|
Body::Ship(ship::Body::DefaultAirship) => Some(1.0),
|
2021-03-12 14:56:47 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
2020-11-03 22:46:07 +00:00
|
|
|
}
|
2020-11-14 17:00:32 +00:00
|
|
|
|
2021-03-13 20:05:02 +00:00
|
|
|
pub fn can_jump(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Body::Object(_) | Body::Ship(_) => false,
|
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 11:50:48 +00:00
|
|
|
pub fn can_climb(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
2020-07-03 18:30:41 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 17:38:18 +00:00
|
|
|
/// Handles updating `Components` to move player based on state of `JoinData`
|
2020-03-26 19:02:01 +00:00
|
|
|
pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
2020-11-06 15:33:20 +00:00
|
|
|
if let Some(depth) = data.physics.in_liquid {
|
2020-08-11 11:13:18 +00:00
|
|
|
swim_move(data, update, efficiency, depth);
|
2021-03-14 20:35:28 +00:00
|
|
|
} else if input_is_pressed(data, InputKind::Fly)
|
2021-03-14 18:42:39 +00:00
|
|
|
&& !data.physics.on_ground
|
2021-03-12 14:56:47 +00:00
|
|
|
&& data.body.can_fly().is_some()
|
2021-03-14 18:42:39 +00:00
|
|
|
{
|
2021-03-12 14:56:47 +00:00
|
|
|
fly_move(data, update, efficiency * data.body.can_fly().expect("can_fly is_some right above this"));
|
2020-03-07 18:15:02 +00:00
|
|
|
} else {
|
2020-03-26 19:02:01 +00:00
|
|
|
basic_move(data, update, efficiency);
|
2020-03-07 18:15:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 17:38:18 +00:00
|
|
|
/// Updates components to move player as if theyre on ground or in air
|
2020-06-10 19:47:36 +00:00
|
|
|
#[allow(clippy::assign_op_pattern)] // TODO: Pending review in #587
|
2020-03-26 19:02:01 +00:00
|
|
|
fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
2020-07-03 18:30:41 +00:00
|
|
|
let accel = if data.physics.on_ground {
|
|
|
|
data.body.base_accel()
|
2020-01-21 22:54:32 +00:00
|
|
|
} else {
|
2020-07-03 18:30:41 +00:00
|
|
|
BASE_HUMANOID_AIR_ACCEL
|
2020-01-21 22:54:32 +00:00
|
|
|
};
|
|
|
|
|
2020-07-03 18:30:41 +00:00
|
|
|
update.vel.0 =
|
2020-07-03 19:30:21 +00:00
|
|
|
update.vel.0 + Vec2::broadcast(data.dt.0) * data.inputs.move_dir * accel * efficiency;
|
2020-01-21 22:54:32 +00:00
|
|
|
|
2020-07-07 00:01:39 +00:00
|
|
|
handle_orientation(data, update, data.body.base_ori_rate());
|
2020-03-20 22:03:29 +00:00
|
|
|
}
|
|
|
|
|
2020-10-25 20:22:40 +00:00
|
|
|
/// Handles forced movement
|
|
|
|
pub fn handle_forced_movement(
|
|
|
|
data: &JoinData,
|
|
|
|
update: &mut StateUpdate,
|
|
|
|
movement: ForcedMovement,
|
|
|
|
efficiency: f32,
|
|
|
|
) {
|
|
|
|
match movement {
|
|
|
|
ForcedMovement::Forward { strength } => {
|
|
|
|
let accel = if data.physics.on_ground {
|
|
|
|
data.body.base_accel()
|
|
|
|
} else {
|
|
|
|
BASE_HUMANOID_AIR_ACCEL
|
|
|
|
};
|
|
|
|
|
|
|
|
update.vel.0 += Vec2::broadcast(data.dt.0)
|
|
|
|
* accel
|
2021-02-04 09:17:38 +00:00
|
|
|
* (data.inputs.move_dir * efficiency + Vec2::from(update.ori) * strength);
|
2020-10-25 20:22:40 +00:00
|
|
|
},
|
|
|
|
ForcedMovement::Leap {
|
|
|
|
vertical,
|
|
|
|
forward,
|
|
|
|
progress,
|
2020-11-27 00:23:15 +00:00
|
|
|
direction,
|
2020-10-25 20:22:40 +00:00
|
|
|
} => {
|
2020-11-27 00:23:15 +00:00
|
|
|
let dir = direction.get_2d_dir(data);
|
2020-10-25 20:22:40 +00:00
|
|
|
// Apply jumping force
|
|
|
|
update.vel.0 = Vec3::new(
|
2020-11-27 00:23:15 +00:00
|
|
|
dir.x,
|
|
|
|
dir.y,
|
2020-10-25 20:22:40 +00:00
|
|
|
vertical,
|
|
|
|
)
|
|
|
|
// Multiply decreasing amount linearly over time (with average of 1)
|
|
|
|
* 2.0 * progress
|
2020-11-27 00:23:15 +00:00
|
|
|
// Apply direction
|
|
|
|
+ Vec3::from(dir)
|
2020-10-25 20:22:40 +00:00
|
|
|
// Multiply by forward leap strength
|
|
|
|
* forward
|
|
|
|
// Control forward movement based on look direction.
|
|
|
|
// This allows players to stop moving forward when they
|
|
|
|
// look downward at target
|
|
|
|
* (1.0 - data.inputs.look_dir.z.abs());
|
|
|
|
},
|
|
|
|
ForcedMovement::Hover { move_input } => {
|
|
|
|
update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0)
|
|
|
|
+ move_input * data.inputs.move_dir.try_normalized().unwrap_or_default();
|
|
|
|
},
|
|
|
|
}
|
2020-09-10 02:58:28 +00:00
|
|
|
handle_orientation(data, update, data.body.base_ori_rate() * efficiency);
|
2020-09-09 00:28:59 +00:00
|
|
|
}
|
|
|
|
|
2020-07-07 00:01:39 +00:00
|
|
|
pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, rate: f32) {
|
2020-01-21 22:54:32 +00:00
|
|
|
// Set direction based on move direction
|
2020-11-24 23:23:29 +00:00
|
|
|
let ori_dir = if (update.character.is_aimed() && data.body.can_strafe())
|
2020-11-20 01:42:22 +00:00
|
|
|
|| update.character.is_attack()
|
|
|
|
{
|
2020-04-25 01:23:49 +00:00
|
|
|
data.inputs.look_dir.xy()
|
|
|
|
} else if !data.inputs.move_dir.is_approx_zero() {
|
|
|
|
data.inputs.move_dir
|
2020-01-21 22:54:32 +00:00
|
|
|
} else {
|
2021-02-04 09:17:38 +00:00
|
|
|
update.ori.into()
|
2020-01-21 22:54:32 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Smooth orientation
|
2021-02-04 09:17:38 +00:00
|
|
|
update.ori = Dir::slerp_to_vec3(update.ori.look_dir(), ori_dir.into(), rate * data.dt.0).into();
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
|
2020-03-08 17:38:18 +00:00
|
|
|
/// Updates components to move player as if theyre swimming
|
2020-08-11 11:13:18 +00:00
|
|
|
fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, depth: f32) {
|
2020-03-07 18:15:02 +00:00
|
|
|
// Update velocity
|
|
|
|
update.vel.0 += Vec2::broadcast(data.dt.0)
|
|
|
|
* data.inputs.move_dir
|
2020-11-25 00:28:24 +00:00
|
|
|
* if update.vel.0.magnitude_squared() < BASE_HUMANOID_WATER_SPEED.powi(2) {
|
2020-03-08 15:38:53 +00:00
|
|
|
BASE_HUMANOID_WATER_ACCEL
|
2020-03-07 18:15:02 +00:00
|
|
|
} else {
|
|
|
|
0.0
|
2020-03-26 19:02:01 +00:00
|
|
|
}
|
|
|
|
* efficiency;
|
2020-03-07 18:15:02 +00:00
|
|
|
|
2020-03-21 14:06:35 +00:00
|
|
|
handle_orientation(data, update, if data.physics.on_ground { 9.0 } else { 2.0 });
|
2020-01-21 22:54:32 +00:00
|
|
|
|
2020-03-23 11:50:08 +00:00
|
|
|
// Swim
|
2020-11-03 22:46:07 +00:00
|
|
|
update.vel.0.z = (update.vel.0.z
|
|
|
|
+ data.dt.0
|
|
|
|
* GRAVITY
|
|
|
|
* 4.0
|
|
|
|
* data
|
|
|
|
.inputs
|
|
|
|
.move_z
|
2020-11-25 00:28:24 +00:00
|
|
|
.clamped(-1.0, depth.clamped(0.0, 1.0).powi(3)))
|
2020-11-03 22:46:07 +00:00
|
|
|
.min(BASE_HUMANOID_WATER_SPEED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Updates components to move entity as if it's flying
|
|
|
|
fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
|
|
|
// Update velocity (counteract gravity with lift)
|
|
|
|
// TODO: Do this better
|
2021-03-13 14:25:56 +00:00
|
|
|
// A loss factor is needed to counteract the very slight deviation in gravity
|
|
|
|
// due to precision issues
|
|
|
|
const LOSS_FACTOR: f32 = 0.995;
|
|
|
|
update.vel.0 += Vec3::unit_z() * data.dt.0 * GRAVITY * LOSS_FACTOR
|
2020-11-03 22:46:07 +00:00
|
|
|
+ Vec3::new(
|
|
|
|
data.inputs.move_dir.x,
|
|
|
|
data.inputs.move_dir.y,
|
|
|
|
data.inputs.move_z,
|
|
|
|
) * data.dt.0
|
|
|
|
* BASE_FLIGHT_ACCEL
|
|
|
|
* efficiency;
|
|
|
|
|
|
|
|
handle_orientation(data, update, 1.0);
|
2020-03-07 18:15:02 +00:00
|
|
|
}
|
|
|
|
|
2021-03-14 20:35:28 +00:00
|
|
|
/// Checks if an input related to an attack is held. If one is, moves entity
|
|
|
|
/// into wielding state
|
2020-06-12 16:07:38 +00:00
|
|
|
pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) {
|
2021-03-14 20:35:28 +00:00
|
|
|
if data.controller.queued_inputs.iter().any(|i| i.is_ability()) {
|
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) {
|
2021-02-17 04:23:08 +00:00
|
|
|
if let Some((item, ItemKind::Tool(tool))) = data
|
2021-01-08 19:12:09 +00:00
|
|
|
.inventory
|
|
|
|
.equipped(EquipSlot::Mainhand)
|
2021-02-17 04:23:08 +00:00
|
|
|
.map(|i| (i, i.kind()))
|
2021-01-08 19:12:09 +00:00
|
|
|
{
|
2020-03-14 21:17:27 +00:00
|
|
|
update.character = CharacterState::Equipping(equipping::Data {
|
2020-10-17 02:29:14 +00:00
|
|
|
static_data: equipping::StaticData {
|
2021-02-23 20:29:27 +00:00
|
|
|
buildup_duration: tool.equip_time(data.msm, item.components()),
|
2020-10-17 02:29:14 +00:00
|
|
|
},
|
|
|
|
timer: Duration::default(),
|
2020-03-14 21:17:27 +00:00
|
|
|
});
|
2020-03-08 17:04:26 +00:00
|
|
|
} else {
|
2021-02-12 23:09:05 +00:00
|
|
|
update.character = CharacterState::Wielding;
|
2020-03-08 17:04:26 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-03-08 17:38:18 +00:00
|
|
|
/// Checks that player can `Sit` and updates `CharacterState` if so
|
2020-03-24 07:38:16 +00:00
|
|
|
pub fn attempt_sit(data: &JoinData, update: &mut StateUpdate) {
|
2020-07-29 02:44:47 +00:00
|
|
|
if data.physics.on_ground {
|
2020-03-21 22:55:20 +00:00
|
|
|
update.character = CharacterState::Sit;
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 06:41:55 +00:00
|
|
|
pub fn attempt_dance(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if data.physics.on_ground && data.body.is_humanoid() {
|
|
|
|
update.character = CharacterState::Dance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-31 20:29:50 +00:00
|
|
|
pub fn attempt_talk(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if data.physics.on_ground {
|
|
|
|
update.character = CharacterState::Talk;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-02 05:09:11 +00:00
|
|
|
pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if data.physics.on_ground && data.body.is_humanoid() {
|
|
|
|
update.character = CharacterState::Sneak;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 17:38:18 +00:00
|
|
|
/// Checks that player can `Climb` and updates `CharacterState` if so
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) {
|
2020-03-24 07:38:16 +00:00
|
|
|
if data.inputs.climb.is_some()
|
2020-03-07 18:15:02 +00:00
|
|
|
&& data.physics.on_wall.is_some()
|
|
|
|
&& !data.physics.on_ground
|
2020-08-24 01:31:05 +00:00
|
|
|
&& !data
|
|
|
|
.physics
|
2020-11-06 15:33:20 +00:00
|
|
|
.in_liquid
|
2020-08-24 01:31:05 +00:00
|
|
|
.map(|depth| depth > 1.0)
|
|
|
|
.unwrap_or(false)
|
2020-02-03 19:43:36 +00:00
|
|
|
//&& update.vel.0.z < 0.0
|
2020-11-14 17:00:32 +00:00
|
|
|
&& data.body.can_climb()
|
2020-03-10 16:33:36 +00:00
|
|
|
&& update.energy.current() > 100
|
2020-01-21 22:54:32 +00:00
|
|
|
{
|
2020-03-21 22:55:20 +00:00
|
|
|
update.character = CharacterState::Climb;
|
2020-01-21 22:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-21 21:16:26 +00:00
|
|
|
/// Checks that player can Swap Weapons and updates `Loadout` if so
|
2021-02-08 17:31:17 +00:00
|
|
|
pub fn attempt_swap_equipped_weapons(data: &JoinData, update: &mut StateUpdate) {
|
2021-01-08 19:12:09 +00:00
|
|
|
if data.inventory.equipped(EquipSlot::Offhand).is_some() {
|
2021-02-08 17:31:17 +00:00
|
|
|
update.swap_equipped_weapons = true;
|
2020-03-25 14:58:26 +00:00
|
|
|
}
|
2020-03-21 21:16:26 +00:00
|
|
|
}
|
|
|
|
|
2021-02-07 02:15:29 +00:00
|
|
|
/// Handles inventory manipulations that affect the loadout
|
2021-02-08 17:31:17 +00:00
|
|
|
pub fn handle_manipulate_loadout(
|
2021-02-07 17:15:29 +00:00
|
|
|
data: &JoinData,
|
|
|
|
update: &mut StateUpdate,
|
2021-03-01 22:40:42 +00:00
|
|
|
inv_action: InventoryAction,
|
2021-02-07 17:15:29 +00:00
|
|
|
) {
|
2021-03-01 22:40:42 +00:00
|
|
|
update
|
|
|
|
.server_events
|
|
|
|
.push_front(ServerEvent::InventoryManip(data.entity, inv_action.into()));
|
2021-02-07 02:15:29 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 21:32:39 +00:00
|
|
|
/// Checks that player can wield the glider and updates `CharacterState` if so
|
|
|
|
pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) {
|
2021-01-08 19:12:09 +00:00
|
|
|
if data.inventory.equipped(EquipSlot::Glider).is_some()
|
2020-08-12 14:10:12 +00:00
|
|
|
&& !data
|
|
|
|
.physics
|
2020-11-06 15:33:20 +00:00
|
|
|
.in_liquid
|
2020-08-12 14:10:12 +00:00
|
|
|
.map(|depth| depth > 1.0)
|
|
|
|
.unwrap_or(false)
|
|
|
|
&& data.body.is_humanoid()
|
|
|
|
{
|
2020-06-16 21:32:39 +00:00
|
|
|
update.character = CharacterState::GlideWield;
|
2020-02-03 10:54:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 17:38:18 +00:00
|
|
|
/// Checks that player can jump and sends jump event if so
|
2020-03-07 18:15:02 +00:00
|
|
|
pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) {
|
2021-03-14 20:35:28 +00:00
|
|
|
if input_is_pressed(data, InputKind::Jump)
|
2020-08-12 14:10:12 +00:00
|
|
|
&& data.physics.on_ground
|
|
|
|
&& !data
|
|
|
|
.physics
|
2020-11-06 15:33:20 +00:00
|
|
|
.in_liquid
|
2020-08-12 14:10:12 +00:00
|
|
|
.map(|depth| depth > 1.0)
|
|
|
|
.unwrap_or(false)
|
2021-03-13 20:05:02 +00:00
|
|
|
&& data.body.can_jump()
|
2020-08-12 14:10:12 +00:00
|
|
|
{
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 20:35:28 +00:00
|
|
|
fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
2021-02-19 23:45:48 +00:00
|
|
|
let hands = |equip_slot| match data.inventory.equipped(equip_slot).map(|i| i.kind()) {
|
|
|
|
Some(ItemKind::Tool(tool)) => Some(tool.hands),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Mouse1 and Skill1 always use the MainHand slot
|
2021-03-14 18:42:39 +00:00
|
|
|
let always_main_hand = matches!(input, InputKind::Primary | InputKind::Ability(0));
|
2021-02-19 23:45:48 +00:00
|
|
|
// skill_index used to select ability for the AbilityKey::Skill2 input
|
|
|
|
let (equip_slot, skill_index) = if always_main_hand {
|
|
|
|
(Some(EquipSlot::Mainhand), 0)
|
|
|
|
} else {
|
|
|
|
let hands = (hands(EquipSlot::Mainhand), hands(EquipSlot::Offhand));
|
|
|
|
match hands {
|
|
|
|
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
|
|
|
(_, Some(Hands::One)) => (Some(EquipSlot::Offhand), 0),
|
|
|
|
(Some(Hands::One), _) => (Some(EquipSlot::Mainhand), 1),
|
|
|
|
(_, _) => (None, 0),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let unlocked = |(s, a): (Option<Skill>, CharacterAbility)| {
|
|
|
|
s.map_or(true, |s| data.stats.skill_set.has_skill(s))
|
|
|
|
.then_some(a)
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(equip_slot) = equip_slot {
|
2020-03-17 14:01:41 +00:00
|
|
|
if let Some(ability) = data
|
2021-01-08 19:12:09 +00:00
|
|
|
.inventory
|
2021-02-19 23:45:48 +00:00
|
|
|
.equipped(equip_slot)
|
|
|
|
.map(|i| &i.item_config_expect().abilities)
|
2021-03-14 18:42:39 +00:00
|
|
|
.and_then(|abilities| match input {
|
|
|
|
InputKind::Primary => Some(abilities.primary.clone()),
|
|
|
|
InputKind::Secondary => Some(abilities.secondary.clone()),
|
|
|
|
InputKind::Ability(0) => abilities.abilities.get(0).cloned().and_then(unlocked),
|
|
|
|
InputKind::Ability(_) => abilities
|
2021-02-19 23:45:48 +00:00
|
|
|
.abilities
|
|
|
|
.get(skill_index)
|
|
|
|
.cloned()
|
|
|
|
.and_then(unlocked),
|
2021-03-14 20:35:28 +00:00
|
|
|
InputKind::Roll | InputKind::Jump | InputKind::Fly => None,
|
2021-02-19 23:45:48 +00:00
|
|
|
})
|
2021-02-12 23:09:05 +00:00
|
|
|
.map(|a| {
|
2021-02-19 23:45:48 +00:00
|
|
|
let tool = unwrap_tool_data(data, equip_slot).map(|t| t.kind);
|
|
|
|
a.adjusted_by_skills(&data.stats.skill_set, tool)
|
2020-12-07 03:35:29 +00:00
|
|
|
})
|
2020-08-06 08:04:03 +00:00
|
|
|
.filter(|ability| ability.requirements_paid(data, update))
|
2020-03-17 14:01:41 +00:00
|
|
|
{
|
2021-02-15 05:07:00 +00:00
|
|
|
update.character = (
|
|
|
|
&ability,
|
2021-03-14 18:42:39 +00:00
|
|
|
AbilityInfo::from_input(data, matches!(equip_slot, EquipSlot::Offhand), input),
|
2021-02-15 05:07:00 +00:00
|
|
|
)
|
|
|
|
.into();
|
2020-02-03 10:54:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 20:35:28 +00:00
|
|
|
pub fn handle_ability_input(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
if let Some(input) = data
|
|
|
|
.controller
|
|
|
|
.queued_inputs
|
|
|
|
.iter()
|
|
|
|
.find(|i| i.is_ability())
|
|
|
|
{
|
|
|
|
handle_ability(data, update, *input);
|
2020-03-24 12:59:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 06:09:56 +00:00
|
|
|
pub fn handle_input(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
|
|
|
match input {
|
2021-03-14 18:42:39 +00:00
|
|
|
InputKind::Primary | InputKind::Secondary | InputKind::Ability(_) => {
|
2021-03-14 20:35:28 +00:00
|
|
|
handle_ability(data, update, input)
|
2021-03-13 00:38:20 +00:00
|
|
|
},
|
2021-03-14 18:42:39 +00:00
|
|
|
InputKind::Roll => handle_dodge_input(data, update),
|
|
|
|
InputKind::Jump => handle_jump(data, update),
|
|
|
|
InputKind::Fly => {},
|
2020-02-24 14:35:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 15:19:22 +00:00
|
|
|
pub fn attempt_input(data: &JoinData, update: &mut StateUpdate) {
|
|
|
|
// TODO: look into using first() when it becomes stable
|
|
|
|
if let Some(input) = data.controller.queued_inputs.iter().next() {
|
|
|
|
handle_input(data, update, *input);
|
2021-02-13 00:43:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-08 17:38:18 +00:00
|
|
|
/// Checks that player can perform a dodge, then
|
2021-01-08 19:12:09 +00:00
|
|
|
/// attempts to perform their dodge ability
|
2020-03-08 17:38:18 +00:00
|
|
|
pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
|
2021-03-14 18:42:39 +00:00
|
|
|
if input_is_pressed(data, InputKind::Roll) && data.body.is_humanoid() {
|
2020-03-17 14:01:41 +00:00
|
|
|
if let Some(ability) = data
|
2021-01-08 19:12:09 +00:00
|
|
|
.inventory
|
|
|
|
.equipped(EquipSlot::Mainhand)
|
2020-12-31 18:37:25 +00:00
|
|
|
.and_then(|i| {
|
2021-01-16 17:01:57 +00:00
|
|
|
i.item_config_expect()
|
|
|
|
.dodge_ability
|
|
|
|
.as_ref()
|
|
|
|
.map(|a| a.clone().adjusted_by_skills(&data.stats.skill_set, None))
|
2020-12-31 18:37:25 +00:00
|
|
|
})
|
2020-08-06 08:04:03 +00:00
|
|
|
.filter(|ability| ability.requirements_paid(data, update))
|
2020-03-08 17:38:18 +00:00
|
|
|
{
|
2021-02-28 00:09:51 +00:00
|
|
|
if let CharacterState::ComboMelee(c) = data.character {
|
|
|
|
update.character = (
|
|
|
|
&ability,
|
2021-03-14 18:42:39 +00:00
|
|
|
AbilityInfo::from_input(data, false, InputKind::Roll),
|
2021-02-28 00:09:51 +00:00
|
|
|
)
|
|
|
|
.into();
|
|
|
|
if let CharacterState::Roll(roll) = &mut update.character {
|
2021-03-14 18:42:39 +00:00
|
|
|
roll.was_combo = Some((c.static_data.ability_info.input, c.stage));
|
2021-02-28 00:09:51 +00:00
|
|
|
roll.was_wielded = true;
|
|
|
|
}
|
|
|
|
} else if data.character.is_wield() {
|
2021-02-15 05:07:00 +00:00
|
|
|
update.character = (
|
|
|
|
&ability,
|
2021-03-14 18:42:39 +00:00
|
|
|
AbilityInfo::from_input(data, false, InputKind::Roll),
|
2021-02-15 05:07:00 +00:00
|
|
|
)
|
|
|
|
.into();
|
2020-03-26 13:46:08 +00:00
|
|
|
if let CharacterState::Roll(roll) = &mut update.character {
|
|
|
|
roll.was_wielded = true;
|
|
|
|
}
|
2020-11-03 04:09:38 +00:00
|
|
|
} else if data.character.is_stealthy() {
|
2021-02-15 05:07:00 +00:00
|
|
|
update.character = (
|
|
|
|
&ability,
|
2021-03-14 18:42:39 +00:00
|
|
|
AbilityInfo::from_input(data, false, InputKind::Roll),
|
2021-02-15 05:07:00 +00:00
|
|
|
)
|
|
|
|
.into();
|
2020-11-03 04:09:38 +00:00
|
|
|
if let CharacterState::Roll(roll) = &mut update.character {
|
|
|
|
roll.was_sneak = true;
|
|
|
|
}
|
2020-03-26 13:46:08 +00:00
|
|
|
} else {
|
2021-02-15 05:07:00 +00:00
|
|
|
update.character = (
|
|
|
|
&ability,
|
2021-03-14 18:42:39 +00:00
|
|
|
AbilityInfo::from_input(data, false, InputKind::Roll),
|
2021-02-15 05:07:00 +00:00
|
|
|
)
|
|
|
|
.into();
|
2020-03-26 13:46:08 +00:00
|
|
|
}
|
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
|
|
|
|
2021-02-14 06:01:53 +00:00
|
|
|
pub fn unwrap_tool_data<'a>(data: &'a JoinData, equip_slot: EquipSlot) -> Option<&'a Tool> {
|
2021-02-15 05:07:00 +00:00
|
|
|
if let Some(ItemKind::Tool(tool)) = data.inventory.equipped(equip_slot).map(|i| i.kind()) {
|
2021-01-08 19:12:09 +00:00
|
|
|
Some(&tool)
|
2020-03-14 18:50:07 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2020-09-10 02:58:28 +00:00
|
|
|
|
2021-03-09 03:53:58 +00:00
|
|
|
pub fn get_crit_data(data: &JoinData, ai: AbilityInfo) -> (f32, f32) {
|
|
|
|
const DEFAULT_CRIT_DATA: (f32, f32) = (0.5, 1.3);
|
|
|
|
use HandInfo::*;
|
|
|
|
let slot = match ai.hand {
|
|
|
|
Some(TwoHanded) | Some(MainHand) => EquipSlot::Mainhand,
|
|
|
|
Some(OffHand) => EquipSlot::Offhand,
|
|
|
|
None => return DEFAULT_CRIT_DATA,
|
|
|
|
};
|
|
|
|
if let Some(item) = data.inventory.equipped(slot) {
|
|
|
|
if let ItemKind::Tool(tool) = item.kind() {
|
|
|
|
let crit_chance = tool.base_crit_chance(data.msm, item.components());
|
|
|
|
let crit_mult = tool.base_crit_mult(data.msm, item.components());
|
|
|
|
return (crit_chance, crit_mult);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DEFAULT_CRIT_DATA
|
|
|
|
}
|
|
|
|
|
2021-03-14 20:35:28 +00:00
|
|
|
pub fn handle_state_interrupt(data: &JoinData, update: &mut StateUpdate, attacks_interrupt: bool) {
|
2020-11-20 17:54:56 +00:00
|
|
|
if attacks_interrupt {
|
2021-03-14 20:35:28 +00:00
|
|
|
handle_ability_input(data, update);
|
2020-11-20 17:30:48 +00:00
|
|
|
}
|
2020-09-20 17:23:33 +00:00
|
|
|
handle_dodge_input(data, update);
|
|
|
|
}
|
|
|
|
|
2021-03-14 18:42:39 +00:00
|
|
|
pub fn input_is_pressed(data: &JoinData, input: InputKind) -> bool {
|
|
|
|
data.controller.queued_inputs.contains(&input)
|
2020-10-07 02:32:57 +00:00
|
|
|
}
|
|
|
|
|
2020-09-10 02:58:28 +00:00
|
|
|
/// Determines what portion a state is in. Used in all attacks (eventually). Is
|
|
|
|
/// used to control aspects of animation code, as well as logic within the
|
|
|
|
/// character states.
|
2020-09-13 01:33:46 +00:00
|
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
2020-09-10 02:58:28 +00:00
|
|
|
pub enum StageSection {
|
|
|
|
Buildup,
|
|
|
|
Swing,
|
|
|
|
Recover,
|
|
|
|
Charge,
|
2020-09-25 20:02:30 +00:00
|
|
|
Cast,
|
2020-09-28 02:38:23 +00:00
|
|
|
Shoot,
|
|
|
|
Movement,
|
2020-09-10 02:58:28 +00:00
|
|
|
}
|
2020-10-07 02:32:57 +00:00
|
|
|
|
2020-10-25 20:22:40 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub enum ForcedMovement {
|
|
|
|
Forward {
|
|
|
|
strength: f32,
|
|
|
|
},
|
|
|
|
Leap {
|
|
|
|
vertical: f32,
|
|
|
|
forward: f32,
|
|
|
|
progress: f32,
|
2020-11-27 00:23:15 +00:00
|
|
|
direction: MovementDirection,
|
2020-10-25 20:22:40 +00:00
|
|
|
},
|
|
|
|
Hover {
|
|
|
|
move_input: f32,
|
|
|
|
},
|
|
|
|
}
|
2020-11-27 00:23:15 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub enum MovementDirection {
|
|
|
|
Look,
|
|
|
|
Move,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MovementDirection {
|
|
|
|
pub fn get_2d_dir(self, data: &JoinData) -> Vec2<f32> {
|
|
|
|
use MovementDirection::*;
|
|
|
|
match self {
|
|
|
|
Look => data.inputs.look_dir.xy(),
|
|
|
|
Move => data.inputs.move_dir,
|
|
|
|
}
|
|
|
|
.try_normalized()
|
|
|
|
.unwrap_or_default()
|
|
|
|
}
|
|
|
|
}
|
2021-02-14 06:01:53 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub struct AbilityInfo {
|
|
|
|
pub tool: Option<ToolKind>,
|
|
|
|
pub hand: Option<HandInfo>,
|
2021-03-14 18:42:39 +00:00
|
|
|
pub input: InputKind,
|
2021-02-14 06:01:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AbilityInfo {
|
2021-03-14 18:42:39 +00:00
|
|
|
pub fn from_input(data: &JoinData, from_offhand: bool, input: InputKind) -> Self {
|
2021-02-14 06:01:53 +00:00
|
|
|
let tool_data = if from_offhand {
|
|
|
|
unwrap_tool_data(data, EquipSlot::Offhand)
|
|
|
|
} else {
|
|
|
|
unwrap_tool_data(data, EquipSlot::Mainhand)
|
|
|
|
};
|
|
|
|
let (tool, hand) = if from_offhand {
|
|
|
|
(tool_data.map(|t| t.kind), Some(HandInfo::OffHand))
|
|
|
|
} else {
|
2021-02-15 05:07:00 +00:00
|
|
|
(
|
|
|
|
tool_data.map(|t| t.kind),
|
|
|
|
tool_data.map(|t| HandInfo::from_main_tool(t)),
|
|
|
|
)
|
2021-02-14 06:01:53 +00:00
|
|
|
};
|
|
|
|
|
2021-03-14 18:42:39 +00:00
|
|
|
Self { tool, hand, input }
|
2021-02-15 05:07:00 +00:00
|
|
|
}
|
2021-02-14 06:01:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub enum HandInfo {
|
|
|
|
TwoHanded,
|
|
|
|
MainHand,
|
|
|
|
OffHand,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl HandInfo {
|
|
|
|
pub fn from_main_tool(tool: &Tool) -> Self {
|
|
|
|
match tool.hands {
|
2021-02-19 23:45:48 +00:00
|
|
|
Hands::Two => Self::TwoHanded,
|
|
|
|
Hands::One => Self::MainHand,
|
2021-02-14 06:01:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|