diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 883740ee51..136913815c 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -452,6 +452,19 @@ pub fn handle_orientation( efficiency: f32, dir_override: Option, ) { + /// first check for horizontal + fn to_horizontal_fast(ori: &crate::comp::Ori) -> crate::comp::Ori { + if ori.to_quat().into_vec4().xy().is_approx_zero() { + *ori + } else { + ori.to_horizontal() + } + } + /// compute an upper limit for the difference of two orientations + fn ori_absdiff(a: &crate::comp::Ori, b: &crate::comp::Ori) -> f32 { + (a.to_quat().into_vec4() - b.to_quat().into_vec4()).reduce(|a, b| a.abs() + b.abs()) + } + // Direction is set to the override if one is provided, else if entity is // strafing or attacking the horiontal component of the look direction is used, // else the current horizontal movement direction is used @@ -465,24 +478,34 @@ pub fn handle_orientation( .into() } else { Dir::from_unnormalized(data.inputs.move_dir.into()) - .map_or_else(|| data.ori.to_horizontal(), |dir| dir.into()) + .map_or_else(|| to_horizontal_fast(data.ori), |dir| dir.into()) }; - let rate = { - // Angle factor used to keep turning rate approximately constant by - // counteracting slerp turning more with a larger angle - let angle_factor = 2.0 / (1.0 - update.ori.dot(target_ori)).sqrt(); - data.body.base_ori_rate() - * efficiency - * angle_factor - * if data.physics.on_ground.is_some() { - 1.0 - } else { - 0.2 - } + // unit is multiples of 180° + let half_turns_per_tick = data.body.base_ori_rate() + * efficiency + * if data.physics.on_ground.is_some() { + 1.0 + } else { + 0.2 + } + * data.dt.0; + // very rough guess + let ticks_from_target_guess = ori_absdiff(&update.ori, &target_ori) / half_turns_per_tick; + let instantaneous = ticks_from_target_guess < 1.0; + update.ori = if instantaneous { + target_ori + } else { + let target_fraction = { + // Angle factor used to keep turning rate approximately constant by + // counteracting slerp turning more with a larger angle + let angle_factor = 2.0 / (1.0 - update.ori.dot(target_ori)).sqrt(); + + half_turns_per_tick * angle_factor + }; + update + .ori + .slerped_towards(target_ori, target_fraction.min(1.0)) }; - update.ori = update - .ori - .slerped_towards(target_ori, (data.dt.0 * rate).min(1.0)); } /// Updates components to move player as if theyre swimming diff --git a/common/systems/Cargo.toml b/common/systems/Cargo.toml index eb09113a64..b2c5fb0b16 100644 --- a/common/systems/Cargo.toml +++ b/common/systems/Cargo.toml @@ -34,4 +34,4 @@ specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "s [dev-dependencies] # Setup a State -common-state = { package = "veloren-common-state", path = "../state" } \ No newline at end of file +common-state = { package = "veloren-common-state", path = "../state" } diff --git a/common/systems/tests/character_state.rs b/common/systems/tests/character_state.rs new file mode 100644 index 0000000000..d2f00609c1 --- /dev/null +++ b/common/systems/tests/character_state.rs @@ -0,0 +1,104 @@ +#[cfg(test)] +mod tests { + use common::{ + comp::{ + item::MaterialStatManifest, skills::GeneralSkill, CharacterState, Controller, Energy, + Ori, PhysicsState, Poise, Pos, Skill, Stats, Vel, + }, + resources::{DeltaTime, GameMode, Time}, + uid::Uid, + util::Dir, + SkillSetBuilder, + }; + use common_ecs::dispatch; + use common_state::State; + use rand::thread_rng; + use specs::{Builder, Entity, WorldExt}; + use std::time::Duration; + use vek::{approx::AbsDiffEq, Vec3}; + use veloren_common_systems::character_behavior; + + fn setup() -> State { + let mut state = State::new(GameMode::Server); + let msm = MaterialStatManifest::load().cloned(); + state.ecs_mut().insert(msm); + state.ecs_mut().read_resource::