mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
No gliding bodies, only gliding glider
This commit is contained in:
parent
906bf798e7
commit
96168b5654
@ -594,21 +594,6 @@ impl Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wings(&self) -> Option<RigidWings> {
|
|
||||||
matches!(
|
|
||||||
self,
|
|
||||||
Body::BirdMedium(_)
|
|
||||||
| Body::BirdSmall(_)
|
|
||||||
| Body::Dragon(_)
|
|
||||||
| Body::FishMedium(_)
|
|
||||||
| Body::FishSmall(_)
|
|
||||||
)
|
|
||||||
.then_some({
|
|
||||||
let dim = self.dimensions().xy();
|
|
||||||
RigidWings::new(dim.x, dim.y * 0.2)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn immune_to(&self, buff: BuffKind) -> bool {
|
pub fn immune_to(&self, buff: BuffKind) -> bool {
|
||||||
match buff {
|
match buff {
|
||||||
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
|
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
|
||||||
@ -665,37 +650,3 @@ impl Body {
|
|||||||
impl Component for Body {
|
impl Component for Body {
|
||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An elliptical fixed rigid wing. Plurally named simply because it's a shape
|
|
||||||
/// typically composed of two wings forming an elliptical lift distribution.
|
|
||||||
///
|
|
||||||
/// Animal wings are technically flexible, not rigid, (difference being that the
|
|
||||||
/// former's shape is affected by the flow) and usually has the ability to
|
|
||||||
/// assume complex shapes with properties like curved camber line, span-wise
|
|
||||||
/// twist, dihedral angle, sweep angle, and partitioned sections. However, we
|
|
||||||
/// can make do with this model for fully extended animal wings, enabling them
|
|
||||||
/// to glide.
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct RigidWings {
|
|
||||||
aspect_ratio: f32,
|
|
||||||
planform_area: f32,
|
|
||||||
// sweep_angle: Option<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RigidWings {
|
|
||||||
/// Wings from total span (wing-tip to wing-tip) and
|
|
||||||
/// chord length (leading edge to trailing edge)
|
|
||||||
pub fn new(span_length: f32, chord_length: f32) -> Self {
|
|
||||||
let planform_area = std::f32::consts::PI * chord_length * span_length * 0.25;
|
|
||||||
Self {
|
|
||||||
aspect_ratio: span_length.powi(2) / planform_area,
|
|
||||||
planform_area,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The aspect ratio is the ratio of the span squared to actual planform
|
|
||||||
/// area
|
|
||||||
pub fn aspect_ratio(&self) -> f32 { self.aspect_ratio }
|
|
||||||
|
|
||||||
pub fn planform_area(&self) -> f32 { self.planform_area }
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{
|
use super::{
|
||||||
body::{object, Body, RigidWings},
|
body::{object, Body},
|
||||||
Density, Ori, Vel,
|
Density, Ori, Vel,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -105,7 +105,6 @@ impl Body {
|
|||||||
0.5 * fluid_density
|
0.5 * fluid_density
|
||||||
* v_sq
|
* v_sq
|
||||||
* wings
|
* wings
|
||||||
.filter(|_| crate::lift_enabled())
|
|
||||||
.map(|wings| {
|
.map(|wings| {
|
||||||
// Since we have wings, we proceed to calculate the lift and drag
|
// Since we have wings, we proceed to calculate the lift and drag
|
||||||
|
|
||||||
@ -268,6 +267,40 @@ fn angle_of_attack(ori: &Ori, rel_flow_dir: &Dir) -> f32 {
|
|||||||
PI / 2.0 - ori.up().angle_between(rel_flow_dir.to_vec())
|
PI / 2.0 - ori.up().angle_between(rel_flow_dir.to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An elliptical fixed rigid wing. Plurally named simply because it's a shape
|
||||||
|
/// typically composed of two wings forming an elliptical lift distribution.
|
||||||
|
//
|
||||||
|
// Animal wings are technically flexible, not rigid, (difference being that the
|
||||||
|
// former's shape is affected by the flow) and usually has the ability to
|
||||||
|
// assume complex shapes with properties like curved camber line, span-wise
|
||||||
|
// twist, dihedral angle, sweep angle, and partitioned sections. However, we
|
||||||
|
// could make do with this model for fully extended animal wings, enabling them
|
||||||
|
// to glide.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct RigidWings {
|
||||||
|
aspect_ratio: f32,
|
||||||
|
planform_area: f32,
|
||||||
|
// sweep_angle: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RigidWings {
|
||||||
|
/// Wings from total span (wing-tip to wing-tip) and
|
||||||
|
/// chord length (leading edge to trailing edge)
|
||||||
|
pub fn new(span_length: f32, chord_length: f32) -> Self {
|
||||||
|
let planform_area = std::f32::consts::PI * chord_length * span_length * 0.25;
|
||||||
|
Self {
|
||||||
|
aspect_ratio: span_length.powi(2) / planform_area,
|
||||||
|
planform_area,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The aspect ratio is the ratio of the span squared to actual planform
|
||||||
|
/// area
|
||||||
|
pub fn aspect_ratio(&self) -> f32 { self.aspect_ratio }
|
||||||
|
|
||||||
|
pub fn planform_area(&self) -> f32 { self.planform_area }
|
||||||
|
}
|
||||||
|
|
||||||
impl RigidWings {
|
impl RigidWings {
|
||||||
/// Total lift coefficient for a finite wing of symmetric aerofoil shape and
|
/// Total lift coefficient for a finite wing of symmetric aerofoil shape and
|
||||||
/// elliptical pressure distribution.
|
/// elliptical pressure distribution.
|
||||||
|
@ -52,7 +52,7 @@ pub use self::{
|
|||||||
body::{
|
body::{
|
||||||
biped_large, biped_small, bird_large, bird_medium, dragon, fish_medium, fish_small, golem,
|
biped_large, biped_small, bird_large, bird_medium, dragon, fish_medium, fish_small, golem,
|
||||||
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod,
|
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod,
|
||||||
AllBodies, Body, BodyData, RigidWings,
|
AllBodies, Body, BodyData,
|
||||||
},
|
},
|
||||||
buff::{
|
buff::{
|
||||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
||||||
@ -68,7 +68,7 @@ pub use self::{
|
|||||||
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
|
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
|
||||||
},
|
},
|
||||||
energy::{Energy, EnergyChange, EnergySource},
|
energy::{Energy, EnergyChange, EnergySource},
|
||||||
fluid_dynamics::Fluid,
|
fluid_dynamics::{Fluid, RigidWings},
|
||||||
group::Group,
|
group::Group,
|
||||||
home_chunk::HomeChunk,
|
home_chunk::HomeChunk,
|
||||||
inputs::CanBuild,
|
inputs::CanBuild,
|
||||||
|
@ -91,5 +91,3 @@ pub use comp::inventory::loadout_builder::LoadoutBuilder;
|
|||||||
pub use explosion::{Explosion, RadiusEffect};
|
pub use explosion::{Explosion, RadiusEffect};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use skillset_builder::SkillSetBuilder;
|
pub use skillset_builder::SkillSetBuilder;
|
||||||
|
|
||||||
pub fn lift_enabled() -> bool { inline_tweak::tweak!(true) }
|
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
use super::utils::handle_climb;
|
use super::utils::handle_climb;
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::{inventory::slot::EquipSlot, CharacterState, Ori, RigidWings, StateUpdate},
|
comp::{inventory::slot::EquipSlot, CharacterState, Ori, RigidWings, StateUpdate},
|
||||||
states::{
|
states::behavior::{CharacterBehavior, JoinData},
|
||||||
behavior::{CharacterBehavior, JoinData},
|
|
||||||
utils::fly_move,
|
|
||||||
},
|
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec2;
|
use vek::*;
|
||||||
|
|
||||||
const GLIDE_ANTIGRAV: f32 = crate::consts::GRAVITY * 0.90;
|
|
||||||
const GLIDE_ACCEL: f32 = 5.0;
|
|
||||||
const GLIDE_MAX_SPEED: f32 = 30.0;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
@ -36,50 +29,69 @@ impl CharacterBehavior for Data {
|
|||||||
// If player is on ground, end glide
|
// If player is on ground, end glide
|
||||||
if data.physics.on_ground && data.vel.0.magnitude_squared() < 25.0 {
|
if data.physics.on_ground && data.vel.0.magnitude_squared() < 25.0 {
|
||||||
update.character = CharacterState::GlideWield;
|
update.character = CharacterState::GlideWield;
|
||||||
return update;
|
update
|
||||||
}
|
} else if data
|
||||||
if data
|
|
||||||
.physics
|
.physics
|
||||||
.in_liquid()
|
.in_liquid()
|
||||||
.map(|depth| depth > 0.5)
|
.map(|depth| depth > 0.5)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
|
|| data.inventory.equipped(EquipSlot::Glider).is_none()
|
||||||
{
|
{
|
||||||
update.character = CharacterState::Idle;
|
update.character = CharacterState::Idle;
|
||||||
}
|
update
|
||||||
if data.inventory.equipped(EquipSlot::Glider).is_none() {
|
} else if handle_climb(&data, &mut update) {
|
||||||
update.character = CharacterState::Idle
|
update
|
||||||
};
|
|
||||||
|
|
||||||
if crate::lift_enabled() {
|
|
||||||
fly_move(data, &mut update, inline_tweak::tweak!(0.1));
|
|
||||||
} else {
|
} else {
|
||||||
let horiz_vel = Vec2::<f32>::from(update.vel.0);
|
let tgt_ori = Some(data.inputs.move_dir)
|
||||||
let horiz_speed_sq = horiz_vel.magnitude_squared();
|
.filter(|mv_dir| !mv_dir.is_approx_zero())
|
||||||
|
.map(|mv_dir| {
|
||||||
|
Vec3::new(
|
||||||
|
mv_dir.x,
|
||||||
|
mv_dir.y,
|
||||||
|
Lerp::lerp_unclamped(
|
||||||
|
0.0,
|
||||||
|
data.inputs.look_dir.z + inline_tweak::tweak!(0.3),
|
||||||
|
mv_dir.magnitude_squared() * inline_tweak::tweak!(2.0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.and_then(Dir::from_unnormalized)
|
||||||
|
.and_then(|tgt_dir| {
|
||||||
|
Dir::from_unnormalized(data.vel.0)
|
||||||
|
.and_then(|moving_dir| moving_dir.to_horizontal())
|
||||||
|
.map(|moving_dir| {
|
||||||
|
Ori::from(tgt_dir).rolled_right(
|
||||||
|
(1.0 - moving_dir.dot(*tgt_dir).max(0.0))
|
||||||
|
* self.ori.right().dot(*tgt_dir).signum()
|
||||||
|
* std::f32::consts::PI
|
||||||
|
/ 3.0,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|| self.ori.look_dir().to_horizontal().map(Ori::from))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Move player according to movement direction vector
|
let rate = {
|
||||||
if horiz_speed_sq < GLIDE_MAX_SPEED.powi(2) {
|
let angle = self.ori.look_dir().angle_between(*data.inputs.look_dir);
|
||||||
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL;
|
0.4 * std::f32::consts::PI / angle
|
||||||
}
|
|
||||||
|
|
||||||
// Determine orientation vector from movement direction vector
|
|
||||||
if let Some(dir) = Dir::from_unnormalized(update.vel.0) {
|
|
||||||
update.ori = update.ori.slerped_towards(Ori::from(dir), 2.0 * data.dt.0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply Glide antigrav lift
|
let ori = self
|
||||||
if update.vel.0.z < 0.0 {
|
.ori
|
||||||
let lift = (GLIDE_ANTIGRAV + update.vel.0.z.powi(2) * 0.15)
|
.slerped_towards(tgt_ori, (data.dt.0 * rate).min(0.1));
|
||||||
* (horiz_speed_sq * f32::powf(0.075, 2.0)).clamp(0.2, 1.0);
|
update.character = CharacterState::Glide(Self { ori, ..*self });
|
||||||
|
|
||||||
update.vel.0.z += lift * data.dt.0;
|
if let Some(char_ori) = ori.to_horizontal() {
|
||||||
|
let rate = {
|
||||||
|
let angle = ori.look_dir().angle_between(*data.inputs.look_dir);
|
||||||
|
data.body.base_ori_rate() * std::f32::consts::PI / angle
|
||||||
|
};
|
||||||
|
update.ori = update
|
||||||
|
.ori
|
||||||
|
.slerped_towards(char_ori, (data.dt.0 * rate).min(0.1));
|
||||||
}
|
}
|
||||||
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a wall in front of character and they are trying to climb go to
|
|
||||||
// climb
|
|
||||||
handle_climb(&data, &mut update);
|
|
||||||
|
|
||||||
update
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwield(&self, data: &JoinData) -> StateUpdate {
|
fn unwield(&self, data: &JoinData) -> StateUpdate {
|
||||||
|
@ -6,7 +6,7 @@ use crate::{
|
|||||||
quadruped_low, quadruped_medium, quadruped_small, ship,
|
quadruped_low, quadruped_medium, quadruped_small, ship,
|
||||||
skills::{Skill, SwimSkill},
|
skills::{Skill, SwimSkill},
|
||||||
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
||||||
InventoryAction, Ori, StateUpdate,
|
InventoryAction, StateUpdate,
|
||||||
},
|
},
|
||||||
consts::{FRIC_GROUND, GRAVITY},
|
consts::{FRIC_GROUND, GRAVITY},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
@ -377,63 +377,14 @@ pub fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) -> b
|
|||||||
.or_else(|| glider.is_some().then_some(0.0))
|
.or_else(|| glider.is_some().then_some(0.0))
|
||||||
{
|
{
|
||||||
let thrust = efficiency * force;
|
let thrust = efficiency * force;
|
||||||
|
|
||||||
let accel = thrust / data.mass.0;
|
let accel = thrust / data.mass.0;
|
||||||
|
|
||||||
// if lift is enabled we do some more advanced stuff with pitch and roll
|
handle_orientation(data, update, efficiency);
|
||||||
if crate::lift_enabled() && !matches!(data.body, Body::Ship(_)) {
|
|
||||||
let mut ori = glider.map(|g| g.ori).unwrap_or(update.ori);
|
|
||||||
let fw_dir = ori.look_dir().to_horizontal();
|
|
||||||
let tgt_ori = Some(data.inputs.move_dir)
|
|
||||||
.filter(|mv_dir| !mv_dir.is_approx_zero())
|
|
||||||
.map(|mv_dir| {
|
|
||||||
Vec3::new(
|
|
||||||
mv_dir.x,
|
|
||||||
mv_dir.y,
|
|
||||||
Lerp::lerp_unclamped(
|
|
||||||
0.0,
|
|
||||||
data.inputs.look_dir.z + inline_tweak::tweak!(0.3),
|
|
||||||
mv_dir.magnitude_squared() * inline_tweak::tweak!(2.0),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.and_then(Dir::from_unnormalized)
|
|
||||||
.and_then(|tgt_dir| {
|
|
||||||
Dir::from_unnormalized(data.vel.0)
|
|
||||||
.and_then(|moving_dir| moving_dir.to_horizontal())
|
|
||||||
.map(|moving_dir| {
|
|
||||||
Ori::from(tgt_dir).rolled_right(
|
|
||||||
(1.0 - moving_dir.dot(*tgt_dir).max(0.0))
|
|
||||||
* ori.right().dot(*tgt_dir).signum()
|
|
||||||
* std::f32::consts::PI
|
|
||||||
/ 3.0,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.or_else(|| fw_dir.map(Ori::from))
|
|
||||||
.unwrap_or_default();
|
|
||||||
let rate = {
|
|
||||||
let angle = ori.look_dir().angle_between(*data.inputs.look_dir);
|
|
||||||
data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle
|
|
||||||
};
|
|
||||||
|
|
||||||
ori = ori.slerped_towards(tgt_ori, (data.dt.0 * rate).min(0.1));
|
|
||||||
if let Some(data) = glider {
|
|
||||||
update.character = CharacterState::Glide(glide::Data { ori, ..*data });
|
|
||||||
if let Some(char_ori) = ori.to_horizontal() {
|
|
||||||
update.ori = char_ori;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
update.ori = ori;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handle_orientation(data, update, efficiency);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elevation control
|
// Elevation control
|
||||||
match data.body {
|
match data.body {
|
||||||
// flappy flappy
|
// flappy flappy
|
||||||
Body::Dragon(_) | Body::BirdMedium(_) | Body::BirdLarge(_) => {
|
Body::Dragon(_) | Body::BirdLarge(_) | Body::BirdMedium(_) => {
|
||||||
let anti_grav = GRAVITY * (1.0 + data.inputs.move_z.min(0.0));
|
let anti_grav = GRAVITY * (1.0 + data.inputs.move_z.min(0.0));
|
||||||
update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0));
|
update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0));
|
||||||
},
|
},
|
||||||
@ -530,7 +481,7 @@ pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that player can `Climb` and updates `CharacterState` if so
|
/// Checks that player can `Climb` and updates `CharacterState` if so
|
||||||
pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) {
|
pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) -> bool {
|
||||||
if data.inputs.climb.is_some()
|
if data.inputs.climb.is_some()
|
||||||
&& data.physics.on_wall.is_some()
|
&& data.physics.on_wall.is_some()
|
||||||
&& !data.physics.on_ground
|
&& !data.physics.on_ground
|
||||||
@ -544,6 +495,9 @@ pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
&& update.energy.current() > 100
|
&& update.energy.current() > 100
|
||||||
{
|
{
|
||||||
update.character = CharacterState::Climb(climb::Data::create_adjusted_by_skills(data));
|
update.character = CharacterState::Climb(climb::Data::create_adjusted_by_skills(data));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,19 +65,12 @@ fn integrate_forces(
|
|||||||
CharacterState::Glide(data) => Some(data),
|
CharacterState::Glide(data) => Some(data),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
// let wings: Option<(RigidWings, Ori)> =
|
|
||||||
// body.wings().map(|w| (w, ori)).or(character_state.and_then(|cs| {
|
|
||||||
// match cs {
|
|
||||||
// CharacterState::Glide(states::glide::Data{wings, ori}) =>
|
|
||||||
// Some((*wings, *ori)), _ => None,
|
|
||||||
// }
|
|
||||||
// }));
|
|
||||||
let impulse = dt.0
|
let impulse = dt.0
|
||||||
* body.aerodynamic_forces(
|
* body.aerodynamic_forces(
|
||||||
glider.map(|g| &g.ori).unwrap_or(ori),
|
glider.map(|g| &g.ori).unwrap_or(ori),
|
||||||
&rel_flow,
|
&rel_flow,
|
||||||
fluid_density.0,
|
fluid_density.0,
|
||||||
glider.map(|g| g.wings).or_else(|| body.wings()).as_ref(),
|
glider.map(|g| g.wings).as_ref(),
|
||||||
);
|
);
|
||||||
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
|
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
|
||||||
if !impulse.is_approx_zero() {
|
if !impulse.is_approx_zero() {
|
||||||
|
Loading…
Reference in New Issue
Block a user