diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 46ff446b85..eb304014a0 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -730,7 +730,7 @@ impl ServerChatCommand { cmd(vec![], "Lightning strike at current position", Some(Admin)) }, ServerChatCommand::Scale => { - cmd(vec![Float("factor", 1.0, Required)], "Scale your character", Some(Admin)) + cmd(vec![Float("factor", 1.0, Required), Boolean("reset_mass", true.to_string(), Optional)], "Scale your character", Some(Admin)) }, } } diff --git a/common/src/comp/fluid_dynamics.rs b/common/src/comp/fluid_dynamics.rs index 9124ae501a..c7b3558497 100644 --- a/common/src/comp/fluid_dynamics.rs +++ b/common/src/comp/fluid_dynamics.rs @@ -135,6 +135,7 @@ impl Body { rel_flow: &Vel, fluid_density: f32, wings: Option<&Wings>, + scale: f32, ) -> Vec3 { let v_sq = rel_flow.0.magnitude_squared(); if v_sq < 0.25 { @@ -201,11 +202,11 @@ impl Body { debug_assert!(c_d.is_sign_positive()); debug_assert!(c_l.is_sign_positive() || aoa.is_sign_negative()); - planform_area * (c_l * *lift_dir + c_d * *rel_flow_dir) - + self.parasite_drag() * *rel_flow_dir + planform_area * scale.powf(2.0) * (c_l * *lift_dir + c_d * *rel_flow_dir) + + self.parasite_drag(scale) * *rel_flow_dir }, - _ => self.parasite_drag() * *rel_flow_dir, + _ => self.parasite_drag(scale) * *rel_flow_dir, } } } @@ -214,13 +215,13 @@ 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(&self) -> f32 { + fn parasite_drag(&self, scale: f32) -> f32 { // Reference area and drag coefficient assumes best-case scenario of the // orientation producing least amount of drag match self { // Cross-section, head/feet first Body::BipedLarge(_) | Body::BipedSmall(_) | Body::Golem(_) | Body::Humanoid(_) => { - let dim = self.dimensions().xy().map(|a| a * 0.5); + let dim = self.dimensions().xy().map(|a| a * 0.5 * scale); const CD: f32 = 0.7; CD * PI * dim.x * dim.y }, @@ -231,7 +232,7 @@ impl Body { | Body::QuadrupedSmall(_) | Body::QuadrupedLow(_) | Body::Arthropod(_) => { - let dim = self.dimensions().map(|a| a * 0.5); + let dim = self.dimensions().map(|a| a * 0.5 * scale); let cd: f32 = if matches!(self, Body::QuadrupedLow(_)) { 0.7 } else { @@ -242,7 +243,7 @@ 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); + let dim = self.dimensions().map(|a| a * 0.5 * scale); let cd: f32 = match self { // "Field Estimates of Body Drag Coefficient // on the Basis of Dives in Passerine Birds", @@ -256,7 +257,7 @@ impl Body { // Cross-section, zero-lift angle; exclude the fins (width * 0.2) Body::FishMedium(_) | Body::FishSmall(_) => { - let dim = self.dimensions().map(|a| a * 0.5); + let dim = self.dimensions().map(|a| a * 0.5 * scale); // "A Simple Method to Determine Drag Coefficients in Aquatic Animals", // D. Bilo and W. Nachtigall, 1980 const CD: f32 = 0.031; @@ -276,7 +277,7 @@ impl Body { | object::Body::FireworkYellow | object::Body::MultiArrow | object::Body::Dart => { - let dim = self.dimensions().map(|a| a * 0.5); + let dim = self.dimensions().map(|a| a * 0.5 * scale); const CD: f32 = 0.02; CD * PI * dim.x * dim.z }, @@ -295,20 +296,20 @@ impl Body { | object::Body::Pumpkin3 | object::Body::Pumpkin4 | object::Body::Pumpkin5 => { - let dim = self.dimensions().map(|a| a * 0.5); + let dim = self.dimensions().map(|a| a * 0.5 * scale); const CD: f32 = 0.5; CD * PI * dim.x * dim.z }, _ => { - let dim = self.dimensions(); + let dim = self.dimensions().map(|a| a * scale); const CD: f32 = 2.0; CD * (PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0) }, }, Body::ItemDrop(_) => { - let dim = self.dimensions(); + let dim = self.dimensions().map(|a| a * scale); const CD: f32 = 2.0; CD * (PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0) }, @@ -316,7 +317,7 @@ impl Body { Body::Ship(_) => { // Airships tend to use the square of the cube root of its volume for // reference area - let dim = self.dimensions(); + let dim = self.dimensions().map(|a| a * scale); (PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0) }, } diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 77ffc8d69d..fa415ce616 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -76,7 +76,7 @@ impl CharacterBehavior for Data { // They've climbed atop something, give them a boost output_events.emit_local(LocalEvent::Jump( data.entity, - CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0, + CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0 * data.scale.map_or(1.0, |s| s.0.powf(13.0).powf(0.25)), )); }; update.character = CharacterState::Idle(idle::Data::default()); diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 2d67ef61be..68c8e06641 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1075,7 +1075,7 @@ pub fn handle_jump( output_events.emit_local(LocalEvent::Jump( data.entity, strength * impulse / data.mass.0 - * data.scale.map_or(1.0, |s| s.0.powf(0.25)) + * data.scale.map_or(1.0, |s| s.0.powf(13.0).powf(0.25)) * data.stats.move_speed_modifier, )); }) diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index e1291a5712..755e555872 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -50,6 +50,7 @@ fn integrate_forces( mass: &Mass, fluid: &Fluid, gravity: f32, + scale: Option, ) -> Vel { let dim = body.dimensions(); let height = dim.z; @@ -61,7 +62,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, wings); + let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0, wings, scale.map_or(1.0, |s| s.0)); debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or()); if !impulse.is_approx_zero() { let new_v = vel.0 + impulse / mass.0; @@ -610,6 +611,7 @@ impl<'a> PhysicsData<'a> { &write.physics_states, &read.masses, &read.densities, + read.scales.maybe(), !&read.is_ridings, ) .par_join() @@ -628,6 +630,7 @@ impl<'a> PhysicsData<'a> { physics_state, mass, density, + scale, _, )| { let in_loaded_chunk = read @@ -672,6 +675,7 @@ impl<'a> PhysicsData<'a> { mass, &fluid, GRAVITY, + scale.copied(), ) .0 }, @@ -1438,7 +1442,8 @@ fn box_voxel_collision + ReadVol>( // Don't jump too far at once const MAX_INCREMENTS: usize = 100; // The maximum number of collision tests per tick - let increments = ((pos_delta.map(|e| e.abs()).reduce_partial_max() / 0.3).ceil() as usize) + let min_step = (radius / 2.0).min(z_max - z_min).clamped(0.01, 0.3); + let increments = ((pos_delta.map(|e| e.abs()).reduce_partial_max() / min_step).ceil() as usize) .clamped(1, MAX_INCREMENTS); let old_pos = pos.0; for _ in 0..increments { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a1e486b3f0..ff72e6c8c8 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1228,6 +1228,7 @@ fn handle_spawn( ) .with(comp::Vel(vel)) .with(opt_scale.map(comp::Scale).unwrap_or(body.scale())) + .maybe_with(opt_scale.map(|s| comp::Mass(body.mass().0 * s.powi(3)))) .with(alignment); if ai { @@ -3844,13 +3845,27 @@ fn handle_scale( args: Vec, action: &ServerChatCommand, ) -> CmdResult<()> { - if let Some(scale) = parse_cmd_args!(args, f32) { + if let (Some(scale), reset_mass) = parse_cmd_args!(args, f32, bool) { let scale = scale.clamped(0.025, 1000.0); let _ = server .state .ecs_mut() .write_storage::() .insert(target, comp::Scale(scale)); + if reset_mass.unwrap_or(true) { + if let Some(body) = server + .state + .ecs() + .read_storage::() + .get(target) + { + let _ = server + .state + .ecs() + .write_storage() + .insert(target, comp::Mass(body.mass().0 * scale.powi(3))); + } + } server.notify_client( client, ServerGeneral::server_msg(ChatType::CommandInfo, format!("Set scale to {}", scale)), diff --git a/server/src/lib.rs b/server/src/lib.rs index e7baa3c55e..03c5ef4fa3 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -709,7 +709,7 @@ impl Server { if old_block.get_rtsim_resource().is_some() || new_block.get_rtsim_resource().is_some() { ecs.write_resource::().hook_block_update( &ecs.read_resource::>(), - ecs.read_resource::>().as_index_ref(), + ecs.read_resource::().as_index_ref(), wpos, old_block, new_block,