diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 21411bd035..be60a777a7 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -240,13 +240,33 @@ impl Body { _ => 200.0, }, Body::QuadrupedSmall(body) => match body.species { - quadruped_small::Species::Batfox => 50.0, + quadruped_small::Species::Axolotl => 1.0, + quadruped_small::Species::Batfox => 10.0, + quadruped_small::Species::Beaver => 10.0, quadruped_small::Species::Boar => 80.0, // ~✅ (60-100 kg) - quadruped_small::Species::Dodarock => 150.0, - quadruped_small::Species::Holladon => 150.0, + quadruped_small::Species::Cat => 4.0, // ~✅ (4-5 kg) + quadruped_small::Species::Dodarock => 70.0, + quadruped_small::Species::Dog => 30.0, // ~✅ (German Shepherd: 30-40 kg) + quadruped_small::Species::Fox => 10.0, + quadruped_small::Species::Frog => 1.0, + quadruped_small::Species::Fungome => 10.0, + quadruped_small::Species::Gecko => 1.0, + quadruped_small::Species::Goat => 50.0, + quadruped_small::Species::Hare => 10.0, + quadruped_small::Species::Holladon => 60.0, quadruped_small::Species::Hyena => 70.0, // ~✅ (vaguely) - quadruped_small::Species::Truffler => 150.0, - _ => 80.0, + quadruped_small::Species::Jackalope => 10.0, + quadruped_small::Species::Pig => 20.0, + quadruped_small::Species::Porcupine => 5.0, + quadruped_small::Species::Quokka => 10.0, + quadruped_small::Species::Rabbit => 2.0, + quadruped_small::Species::Raccoon => 30.0, + quadruped_small::Species::Rat => 1.0, + quadruped_small::Species::Sheep => 50.0, + quadruped_small::Species::Skunk => 5.0, + quadruped_small::Species::Squirrel => 1.0, + quadruped_small::Species::Truffler => 70.0, + quadruped_small::Species::Turtle => 40.0, }, Body::Theropod(body) => match body.species { // for reference, elephants are in the range of 2.6-6.9 tons diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index d5a00fcdc2..6de2c5ca86 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -349,7 +349,7 @@ impl Body { Body::BoltFire => Vec3::new(0.1, 0.1, 0.1), Body::Crossbow => Vec3::new(3.0, 3.0, 1.5), Body::HaniwaSentry => Vec3::new(0.8, 0.8, 1.4), - _ => Vec3::broadcast(0.2), + _ => Vec3::broadcast(0.5), } } } diff --git a/common/src/comp/fluid_dynamics.rs b/common/src/comp/fluid_dynamics.rs index d80a277997..42b60ae0f8 100644 --- a/common/src/comp/fluid_dynamics.rs +++ b/common/src/comp/fluid_dynamics.rs @@ -1,6 +1,6 @@ use super::{ body::{object, Body}, - CharacterState, Density, Ori, Vel, + Density, Ori, Vel, }; use crate::{ consts::{AIR_DENSITY, WATER_DENSITY}, @@ -87,12 +87,22 @@ impl Default for Fluid { } } +pub enum Wings { + Flying, + Gliding { + aspect_ratio: f32, + planform_area: f32, + ori: Ori, + }, + Folded, +} + impl Body { pub fn aerodynamic_forces( &self, rel_flow: &Vel, fluid_density: f32, - character_state: Option<&CharacterState>, + wings: Option<&Wings>, ) -> Vec3 { let v_sq = rel_flow.0.magnitude_squared(); if v_sq < 0.25 { @@ -103,23 +113,24 @@ impl Body { // All the coefficients come pre-multiplied by their reference area 0.5 * fluid_density * v_sq - * character_state - .and_then(|cs| match cs { - CharacterState::Glide(data) => { - Some((data.aspect_ratio, data.planform_area, data.ori)) + * wings + .and_then(|wings| match *wings { + Wings::Gliding { + aspect_ratio, + planform_area, + ori, + } => { + if aspect_ratio > 25.0 { + tracing::warn!( + "Calculating lift for wings with an aspect ratio of {}. The \ + formulas are only valid for aspect ratios below 25.", + aspect_ratio + ) + }; + Some((aspect_ratio.min(24.0), planform_area, ori)) }, _ => None, }) - .map(|(ar, area, ori)| { - if ar > 25.0 { - tracing::warn!( - "Calculating lift for wings with an aspect ratio of {}. The \ - formulas are only valid for aspect ratios below 25.", - ar - ) - }; - (ar.min(24.0), area, ori) - }) .map(|(ar, area, ori)| { // We have an elliptical wing; proceed to calculate its lift and drag @@ -158,7 +169,7 @@ impl Body { let e = 1.78 * (1.0 - 0.045 * ar.powf(0.68)) - 0.64; zero_lift_drag_coefficient(area) - + self.parasite_drag_coefficient() + + self.parasite_drag_coefficient(wings) + c_l.powi(2) / (PI * e * ar) }; debug_assert!(c_d.is_sign_positive()); @@ -166,7 +177,7 @@ impl Body { c_l * *lift_dir + c_d * *rel_flow_dir }) - .unwrap_or_else(|| self.parasite_drag_coefficient() * *rel_flow_dir) + .unwrap_or_else(|| self.parasite_drag_coefficient(wings) * *rel_flow_dir) } } @@ -174,7 +185,7 @@ impl Body { /// Skin friction is the drag arising from the shear forces between a fluid /// and a surface, while pressure drag is due to flow separation. Both are /// viscous effects. - fn parasite_drag_coefficient(&self) -> f32 { + fn parasite_drag_coefficient(&self, wings: Option<&Wings>) -> f32 { // Reference area and drag coefficient assumes best-case scenario of the // orientation producing least amount of drag match self { @@ -201,12 +212,16 @@ impl Body { // Cross-section, zero-lift angle; exclude the wings (width * 0.2) Body::BirdMedium(_) | Body::BirdLarge(_) | Body::Dragon(_) => { let dim = self.dimensions().map(|a| a * 0.5); - // "Field Estimates of Body Drag Coefficient on the Basis of Dives in Passerine - // Birds", Anders Hedenström and Felix Liechti, 2001 - let cd = match self { - Body::BirdLarge(_) | Body::BirdMedium(_) => 0.2, - // arbitrary - _ => 0.7, + let cd = if matches!(wings, Some(Wings::Folded) | None) { + 0.7 + } else { + // "Field Estimates of Body Drag Coefficient on the Basis of Dives in Passerine + // Birds", Anders Hedenström and Felix Liechti, 2001 + match self { + Body::BirdLarge(_) | Body::BirdMedium(_) => 0.2, + // arbitrary + _ => 0.7, + } }; cd * PI * dim.x * 0.2 * dim.z }, diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index 65cddd045d..d4afb8a72a 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -1,14 +1,16 @@ use common::{ comp::{ body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST}, - BeamSegment, Body, CharacterState, Collider, Density, Fluid, Mass, Mounting, Ori, - PhysicsState, Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, Shockwave, Sticky, - Vel, + fluid_dynamics::{Fluid, Wings}, + BeamSegment, Body, CharacterState, Collider, Controller, Density, InputKind, Mass, + Mounting, Ori, PhysicsState, Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, + Shockwave, Sticky, Vel, }, consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY}, event::{EventBus, ServerEvent}, outcome::Outcome, resources::DeltaTime, + states, terrain::{Block, TerrainGrid}, uid::Uid, util::{Projection, SpatialGrid}, @@ -38,14 +40,44 @@ fn fluid_density(height: f32, fluid: &Fluid) -> Density { Density(fluid.density().0 * immersion + AIR_DENSITY * (1.0 - immersion)) } +fn get_wings( + character_state: Option<&CharacterState>, + controller: Option<&Controller>, + body: &Body, +) -> Option { + match *character_state? { + CharacterState::Glide(states::glide::Data { + aspect_ratio, + planform_area, + ori, + .. + }) => Some(Wings::Gliding { + aspect_ratio, + planform_area, + ori, + }), + + _ => { + if body.fly_thrust().is_some() { + if controller?.queued_inputs.contains_key(&InputKind::Fly) { + Some(Wings::Flying) + } else { + Some(Wings::Folded) + } + } else { + None + } + }, + } +} + #[allow(clippy::too_many_arguments)] fn integrate_forces( dt: &DeltaTime, mut vel: Vel, - body: &Body, + (body, wings): (&Body, Option<&Wings>), density: &Density, mass: &Mass, - character_state: Option<&CharacterState>, fluid: &Fluid, gravity: f32, ) -> Vel { @@ -59,7 +91,7 @@ fn integrate_forces( // Aerodynamic/hydrodynamic forces if !rel_flow.0.is_approx_zero() { debug_assert!(!rel_flow.0.map(|a| a.is_nan()).reduce_or()); - let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0, character_state); + let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0, wings); debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or()); if !impulse.is_approx_zero() { let new_v = vel.0 + impulse / mass.0; @@ -116,6 +148,7 @@ pub struct PhysicsRead<'a> { stickies: ReadStorage<'a, Sticky>, masses: ReadStorage<'a, Mass>, colliders: ReadStorage<'a, Collider>, + controllers: ReadStorage<'a, Controller>, mountings: ReadStorage<'a, Mounting>, projectiles: ReadStorage<'a, Projectile>, beams: ReadStorage<'a, BeamSegment>, @@ -574,6 +607,7 @@ impl<'a> PhysicsData<'a> { &read.bodies, read.character_states.maybe(), &write.physics_states, + read.controllers.maybe(), &read.masses, &read.densities, !&read.mountings, @@ -592,6 +626,7 @@ impl<'a> PhysicsData<'a> { body, character_state, physics_state, + controller, mass, density, _, @@ -616,13 +651,13 @@ impl<'a> PhysicsData<'a> { vel.0.z -= dt.0 * GRAVITY; }, Some(fluid) => { + let wings = get_wings(character_state, controller, body); vel.0 = integrate_forces( &dt, *vel, - body, + (body, wings.as_ref()), density, mass, - character_state, &fluid, GRAVITY, ) diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 6a0dbada4d..6de933d705 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -79,6 +79,11 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) if let Some(vel) = velocities.get_mut(entity) { vel.0 += impulse; } + if let Some(controller) = ecs.write_storage::().get_mut(entity) { + controller + .actions + .push(comp::ControlAction::CancelInput(comp::InputKind::Fly)); + } if let Some(client) = clients.get(entity) { client.send_fallible(ServerGeneral::Knockback(impulse)); }