From 5d85775dcf6bb71bca0712af490620433b00f675 Mon Sep 17 00:00:00 2001 From: Avi Weinstock Date: Thu, 11 Mar 2021 17:27:03 -0500 Subject: [PATCH] Airship progress: now possessable, and physics kind of works (desyncs from the visuals by a shift + scale, and rotation isn't done at all yet, but the contours are correct). --- assets/server/manifests/ship_manifest.ron | 11 +- assets/server/voxel/Human_Airship.vox | 4 +- assets/voxygen/voxel/object/Human_Airship.vox | 4 +- common/src/comp/body/ship.rs | 57 +- common/src/comp/phys.rs | 4 +- common/src/volumes/dyna.rs | 10 + common/sys/src/phys.rs | 525 +++++++++--------- server/src/cmd.rs | 7 +- server/src/state_ext.rs | 16 +- voxygen/src/scene/figure/mod.rs | 8 +- 10 files changed, 359 insertions(+), 287 deletions(-) mode change 100644 => 120000 assets/voxygen/voxel/object/Human_Airship.vox diff --git a/assets/server/manifests/ship_manifest.ron b/assets/server/manifests/ship_manifest.ron index 42555fc051..4ccbe5fcc6 100644 --- a/assets/server/manifests/ship_manifest.ron +++ b/assets/server/manifests/ship_manifest.ron @@ -1,16 +1,17 @@ ({ DefaultAirship: ( bone0: ( - offset: (-20.0, -35.0, 1.0), + //offset: (-20.0, -35.0, 1.0), + offset: (3.0, 7.0, 1.0), central: ("object.Human_Airship"), ), bone1: ( - offset: (0.0, 0.0, 0.0), - central: ("propeller-l"), + offset: (0.0, 40.0, -8.0), + central: ("object.propeller-l"), ), bone2: ( - offset: (0.0, 0.0, 0.0), - central: ("propeller-r"), + offset: (0.0, 0.0, -4.0), + central: ("object.propeller-r"), ), ), }) diff --git a/assets/server/voxel/Human_Airship.vox b/assets/server/voxel/Human_Airship.vox index ab1ea75279..36e3758d03 100644 --- a/assets/server/voxel/Human_Airship.vox +++ b/assets/server/voxel/Human_Airship.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6355ef90e28c448e11ad79dc6311388a71b4e46628bb2cf82b6e5fd5f38cd254 -size 88100 +oid sha256:ba02746d73ebf853c0511b673510c09bd47e3ab0fff13d936feb181a8378ebd9 +size 78024 diff --git a/assets/voxygen/voxel/object/Human_Airship.vox b/assets/voxygen/voxel/object/Human_Airship.vox deleted file mode 100644 index 5d32a3168a..0000000000 --- a/assets/voxygen/voxel/object/Human_Airship.vox +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:561dcec86218a94ae85268b3f1863cd3310e24c452dd2ba248e3c37b6aff45e5 -size 78024 diff --git a/assets/voxygen/voxel/object/Human_Airship.vox b/assets/voxygen/voxel/object/Human_Airship.vox new file mode 120000 index 0000000000..9995596018 --- /dev/null +++ b/assets/voxygen/voxel/object/Human_Airship.vox @@ -0,0 +1 @@ +../../../server/voxel/Human_Airship.vox \ No newline at end of file diff --git a/common/src/comp/body/ship.rs b/common/src/comp/body/ship.rs index ef2e93e113..f607cf2ae6 100644 --- a/common/src/comp/body/ship.rs +++ b/common/src/comp/body/ship.rs @@ -1,6 +1,4 @@ -use crate::{ - make_case_elim -}; +use crate::make_case_elim; use serde::{Deserialize, Serialize}; make_case_elim!( @@ -17,22 +15,29 @@ impl From for super::Body { } impl Body { - pub fn manifest_id(&self) -> &'static str { + pub fn manifest_entry(&self) -> &'static str { match self { - Body::DefaultAirship => "server.manifests.ship_manifest", + Body::DefaultAirship => "object.Human_Airship", } } } -/// Duplicate of some of the things defined in `voxygen::scene::figure::load` to avoid having to -/// refactor all of that to `common` for using voxels as collider geometry +/// Duplicate of some of the things defined in `voxygen::scene::figure::load` to +/// avoid having to refactor all of that to `common` for using voxels as +/// collider geometry pub mod figuredata { use crate::{ assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron}, - volumes::dyna::Dyna, + figure::cell::Cell, + terrain::{ + block::{Block, BlockKind}, + sprite::SpriteKind, + }, + volumes::dyna::{ColumnAccess, Dyna}, }; - use serde::Deserialize; use hashbrown::HashMap; + use lazy_static::lazy_static; + use serde::Deserialize; #[derive(Deserialize)] pub struct VoxSimple(pub String); @@ -57,13 +62,43 @@ pub mod figuredata { #[derive(Clone)] pub struct ShipSpec { pub central: AssetHandle>, + pub voxes: HashMap>, } impl assets::Compound for ShipSpec { - fn load(_: &assets::AssetCache, _: &str) -> Result { + fn load( + cache: &assets::AssetCache, + _: &str, + ) -> Result { + let manifest: AssetHandle> = AssetExt::load("server.manifests.ship_manifest")?; + let mut voxes = HashMap::new(); + for (_, spec) in (manifest.read().0).0.iter() { + for bone in [&spec.bone0, &spec.bone1, &spec.bone2].iter() { + // TODO: avoid the requirement for symlinks in "voxygen.voxel.object.", and load + // the models from "server.voxel." instead + let vox = + cache.load::(&["voxygen.voxel.", &bone.central.0].concat())?; + let dyna = Dyna::::from_vox(&vox.read().0, false); + voxes.insert(bone.central.0.clone(), dyna.map_into(|cell| { + if let Some(rgb) = cell.get_color() { + Block::new(BlockKind::Misc, rgb) + } else { + Block::air(SpriteKind::Empty) + } + })); + } + } Ok(ShipSpec { - central: AssetExt::load("server.manifests.ship_manifest")? + central: manifest, + voxes, }) } } + + lazy_static! { + // TODO: load this from the ECS as a resource, and maybe make it more general than ships + // (although figuring out how to keep the figure bones in sync with the terrain offsets seems + // like a hard problem if they're not the same manifest) + pub static ref VOXEL_COLLIDER_MANIFEST: ShipSpec = AssetExt::load_expect_cloned("server.manifests.ship_manifest"); + } } diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 3e06418792..b10978cc58 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -68,7 +68,7 @@ pub enum Collider { impl Collider { pub fn get_radius(&self) -> f32 { match self { - Collider::Voxel { .. } => 0.0, + Collider::Voxel { .. } => 1.0, Collider::Box { radius, .. } => *radius, Collider::Point => 0.0, } @@ -76,7 +76,7 @@ impl Collider { pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) { match self { - Collider::Voxel { .. } => (0.0, 0.0), + Collider::Voxel { .. } => (0.0, 1.0), Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier), Collider::Point => (0.0, 0.0), } diff --git a/common/src/volumes/dyna.rs b/common/src/volumes/dyna.rs index 3e736c5db1..d0648010a8 100644 --- a/common/src/volumes/dyna.rs +++ b/common/src/volumes/dyna.rs @@ -44,6 +44,16 @@ impl Dyna { None } } + + pub fn map_into W>(self, f: F) -> Dyna { + let Dyna { vox, meta, sz, _phantom } = self; + Dyna { + vox: vox.into_iter().map(f).collect(), + meta, + sz, + _phantom, + } + } } impl BaseVol for Dyna { diff --git a/common/sys/src/phys.rs b/common/sys/src/phys.rs index 8545888c7e..47c6abfd1e 100644 --- a/common/sys/src/phys.rs +++ b/common/sys/src/phys.rs @@ -1,7 +1,7 @@ use common::{ comp::{ BeamSegment, CharacterState, Collider, Gravity, Mass, Mounting, Ori, PhysicsState, Pos, - PreviousPhysCache, Projectile, Scale, Shockwave, Sticky, Vel, + PreviousPhysCache, Projectile, Scale, Shockwave, Sticky, Vel, body::ship::figuredata::VOXEL_COLLIDER_MANIFEST, }, consts::{FRIC_GROUND, GRAVITY}, event::{EventBus, ServerEvent}, @@ -15,8 +15,9 @@ use common_ecs::{Job, Origin, ParMode, Phase, PhysicsMetrics, System}; use hashbrown::HashMap; use rayon::iter::ParallelIterator; use specs::{ - shred::{World, ResourceId}, - Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, WriteExpect, WriteStorage, SystemData, + shred::{ResourceId, World}, + Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, WriteExpect, + WriteStorage, }; use std::ops::Range; use vek::*; @@ -114,7 +115,9 @@ impl<'a> PhysicsSystemData<'a> { ) .join() { - let _ = self.w.physics_states + let _ = self + .w + .physics_states .entry(entity) .map(|e| e.or_insert_with(Default::default)); } @@ -137,13 +140,16 @@ impl<'a> PhysicsSystemData<'a> { .map(|(e, _, _, _, _, _, _)| e) .collect::>() { - let _ = self.w.previous_phys_cache.insert(entity, PreviousPhysCache { - velocity_dt: Vec3::zero(), - center: Vec3::zero(), - collision_boundary: 0.0, - scale: 0.0, - scaled_radius: 0.0, - }); + let _ = self + .w + .previous_phys_cache + .insert(entity, PreviousPhysCache { + velocity_dt: Vec3::zero(), + center: Vec3::zero(), + collision_boundary: 0.0, + scale: 0.0, + scaled_radius: 0.0, + }); } //Update PreviousPhysCache @@ -180,10 +186,14 @@ impl<'a> PhysicsSystemData<'a> { } drop(guard); } + fn apply_pushback(&mut self, job: &mut Job) { span!(guard, "Apply pushback"); job.cpu_stats.measure(ParMode::Rayon); - let PhysicsSystemData { r: ref psdr, w: ref mut psdw } = self; + let PhysicsSystemData { + r: ref psdr, + w: ref mut psdw, + } = self; let (positions, previous_phys_cache) = (&psdw.positions, &psdw.previous_phys_cache); let metrics = ( &psdr.entities, @@ -316,8 +326,13 @@ impl<'a> PhysicsSystemData<'a> { entity_entity_collisions += 1; } - // Don't apply repulsive force to projectiles - if diff.magnitude_squared() > 0.0 && !is_projectile { + // Don't apply repulsive force to projectiles or if we're colliding + // with a terrain-like entity, or if we are a terrain-like entity + if diff.magnitude_squared() > 0.0 + && !is_projectile + && !matches!(collider_other, Some(Collider::Voxel { .. })) + && !matches!(collider, Some(Collider::Voxel { .. })) + { let force = 400.0 * (collision_dist - diff.magnitude()) * mass_other / (mass + mass_other); @@ -346,265 +361,273 @@ impl<'a> PhysicsSystemData<'a> { entity_entity_collisions: old.entity_entity_collisions + new.entity_entity_collisions, }); - psdw.physics_metrics.entity_entity_collision_checks = metrics.entity_entity_collision_checks; + psdw.physics_metrics.entity_entity_collision_checks = + metrics.entity_entity_collision_checks; psdw.physics_metrics.entity_entity_collisions = metrics.entity_entity_collisions; drop(guard); } fn handle_movement_and_terrain(&mut self, job: &mut Job) { - let PhysicsSystemData { r: ref psdr, w: ref mut psdw } = self; + let PhysicsSystemData { + r: ref psdr, + w: ref mut psdw, + } = self; // Apply movement inputs span!(guard, "Apply movement and terrain collision"); let (positions, previous_phys_cache) = (&psdw.positions, &psdw.previous_phys_cache); - let (pos_writes, land_on_grounds) = - ( - &psdr.entities, - psdr.scales.maybe(), - psdr.stickies.maybe(), - &psdr.colliders, - positions, - &mut psdw.velocities, - &psdw.orientations, - &mut psdw.physics_states, - previous_phys_cache, - !&psdr.mountings, - ) - .par_join() - .fold( - || (Vec::new(), Vec::new()), - |(mut pos_writes, mut land_on_grounds), - ( - entity, - scale, - sticky, - collider, - pos, - mut vel, - _ori, - mut physics_state, - previous_cache, - _, - )| { - // defer the writes of positions to allow an inner loop over terrain-like - // entities - let old_pos = *pos; - let mut pos = *pos; - if sticky.is_some() && physics_state.on_surface().is_some() { - vel.0 = Vec3::zero(); - return (pos_writes, land_on_grounds); - } + let (pos_writes, land_on_grounds) = ( + &psdr.entities, + psdr.scales.maybe(), + psdr.stickies.maybe(), + &psdr.colliders, + positions, + &mut psdw.velocities, + &psdw.orientations, + &mut psdw.physics_states, + previous_phys_cache, + !&psdr.mountings, + ) + .par_join() + .fold( + || (Vec::new(), Vec::new()), + |(mut pos_writes, mut land_on_grounds), + ( + entity, + scale, + sticky, + collider, + pos, + mut vel, + _ori, + mut physics_state, + previous_cache, + _, + )| { + // defer the writes of positions to allow an inner loop over terrain-like + // entities + let old_pos = *pos; + let mut pos = *pos; + if sticky.is_some() && physics_state.on_surface().is_some() { + vel.0 = Vec3::zero(); + return (pos_writes, land_on_grounds); + } - let scale = if let Collider::Voxel { .. } = collider { - scale.map(|s| s.0).unwrap_or(1.0) + let scale = if let Collider::Voxel { .. } = collider { + scale.map(|s| s.0).unwrap_or(1.0) + } else { + // TODO: Use scale & actual proportions when pathfinding is good + // enough to manage irregular entity sizes + 1.0 + }; + + let old_vel = *vel; + // Integrate forces + // Friction is assumed to be a constant dependent on location + let friction = FRIC_AIR + .max(if physics_state.on_ground { + FRIC_GROUND } else { - // TODO: Use scale & actual proportions when pathfinding is good - // enough to manage irregular entity sizes - 1.0 - }; - - let old_vel = *vel; - // Integrate forces - // Friction is assumed to be a constant dependent on location - let friction = FRIC_AIR - .max(if physics_state.on_ground { - FRIC_GROUND - } else { - 0.0 - }) - .max(if physics_state.in_liquid.is_some() { - FRIC_FLUID - } else { - 0.0 - }); - let in_loaded_chunk = psdr.terrain - .get_key(psdr.terrain.pos_key(pos.0.map(|e| e.floor() as i32))) - .is_some(); - let downward_force = - if !in_loaded_chunk { - 0.0 // No gravity in unloaded chunks - } else if physics_state - .in_liquid - .map(|depth| depth > 0.75) - .unwrap_or(false) - { - (1.0 - BOUYANCY) * GRAVITY - } else { - GRAVITY - } * psdr.gravities.get(entity).map(|g| g.0).unwrap_or_default(); - vel.0 = integrate_forces(psdr.dt.0, vel.0, downward_force, friction); - - // Don't move if we're not in a loaded chunk - let pos_delta = if in_loaded_chunk { - // this is an approximation that allows most framerates to - // behave in a similar manner. - let dt_lerp = 0.2; - (vel.0 * dt_lerp + old_vel.0 * (1.0 - dt_lerp)) * psdr.dt.0 + 0.0 + }) + .max(if physics_state.in_liquid.is_some() { + FRIC_FLUID } else { - Vec3::zero() - }; - - match &*collider { - Collider::Voxel { .. } => { - // for now, treat entities with voxel colliders as their bounding - // cylinders for the purposes of colliding them with terrain - let radius = collider.get_radius() * scale; - let (z_min, z_max) = collider.get_z_limits(scale); - - let cylinder = (radius, z_min, z_max); - cylinder_voxel_collision( - cylinder, - &*psdr.terrain, - entity, - &mut pos, - pos_delta, - vel, - &mut physics_state, - &mut land_on_grounds, - ); - }, - Collider::Box { - radius, - z_min, - z_max, - } => { - // Scale collider - let radius = radius.min(0.45) * scale; - let z_min = *z_min * scale; - let z_max = z_max.clamped(1.2, 1.95) * scale; - - let cylinder = (radius, z_min, z_max); - cylinder_voxel_collision( - cylinder, - &*psdr.terrain, - entity, - &mut pos, - pos_delta, - vel, - &mut physics_state, - &mut land_on_grounds, - ); - }, - Collider::Point => { - let (dist, block) = psdr.terrain - .ray(pos.0, pos.0 + pos_delta) - .until(|block: &Block| block.is_filled()) - .ignore_error() - .cast(); - - pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist; - - // Can't fail since we do ignore_error above - if block.unwrap().is_some() { - let block_center = pos.0.map(|e| e.floor()) + 0.5; - let block_rpos = (pos.0 - block_center) - .try_normalized() - .unwrap_or(Vec3::zero()); - - // See whether we're on the top/bottom of a block, or the side - if block_rpos.z.abs() - > block_rpos.xy().map(|e| e.abs()).reduce_partial_max() - { - if block_rpos.z > 0.0 { - physics_state.on_ground = true; - } else { - physics_state.on_ceiling = true; - } - vel.0.z = 0.0; - } else { - physics_state.on_wall = - Some(if block_rpos.x.abs() > block_rpos.y.abs() { - vel.0.x = 0.0; - Vec3::unit_x() * -block_rpos.x.signum() - } else { - vel.0.y = 0.0; - Vec3::unit_y() * -block_rpos.y.signum() - }); - } - } - - physics_state.in_liquid = psdr.terrain - .get(pos.0.map(|e| e.floor() as i32)) - .ok() - .and_then(|vox| vox.is_liquid().then_some(1.0)); - }, - } - - // Collide with terrain-like entities - for ( - entity_other, - other, - pos_other, - previous_cache_other, - mass_other, - collider_other, - _, - _, - _, - _, - char_state_other_maybe, - ) in ( - &psdr.entities, - &psdr.uids, - positions, - previous_phys_cache, - psdr.masses.maybe(), - &psdr.colliders, - !&psdr.projectiles, - !&psdr.mountings, - !&psdr.beams, - !&psdr.shockwaves, - psdr.char_states.maybe(), - ) - .join() + 0.0 + }); + let in_loaded_chunk = psdr + .terrain + .get_key(psdr.terrain.pos_key(pos.0.map(|e| e.floor() as i32))) + .is_some(); + let downward_force = + if !in_loaded_chunk { + 0.0 // No gravity in unloaded chunks + } else if physics_state + .in_liquid + .map(|depth| depth > 0.75) + .unwrap_or(false) { - let collision_boundary = previous_cache.collision_boundary - + previous_cache_other.collision_boundary; - if previous_cache - .center - .distance_squared(previous_cache_other.center) - > collision_boundary.powi(2) - || entity == entity_other - { - continue; + (1.0 - BOUYANCY) * GRAVITY + } else { + GRAVITY + } * psdr.gravities.get(entity).map(|g| g.0).unwrap_or_default(); + vel.0 = integrate_forces(psdr.dt.0, vel.0, downward_force, friction); + + // Don't move if we're not in a loaded chunk + let pos_delta = if in_loaded_chunk { + // this is an approximation that allows most framerates to + // behave in a similar manner. + let dt_lerp = 0.2; + (vel.0 * dt_lerp + old_vel.0 * (1.0 - dt_lerp)) * psdr.dt.0 + } else { + Vec3::zero() + }; + + match &*collider { + Collider::Voxel { .. } => { + // for now, treat entities with voxel colliders as their bounding + // cylinders for the purposes of colliding them with terrain + let radius = collider.get_radius() * scale; + let (z_min, z_max) = collider.get_z_limits(scale); + + let cylinder = (radius, z_min, z_max); + cylinder_voxel_collision( + cylinder, + &*psdr.terrain, + entity, + &mut pos, + pos_delta, + vel, + &mut physics_state, + &mut land_on_grounds, + ); + }, + Collider::Box { + radius, + z_min, + z_max, + } => { + // Scale collider + let radius = radius.min(0.45) * scale; + let z_min = *z_min * scale; + let z_max = z_max.clamped(1.2, 1.95) * scale; + + let cylinder = (radius, z_min, z_max); + cylinder_voxel_collision( + cylinder, + &*psdr.terrain, + entity, + &mut pos, + pos_delta, + vel, + &mut physics_state, + &mut land_on_grounds, + ); + }, + Collider::Point => { + let (dist, block) = psdr + .terrain + .ray(pos.0, pos.0 + pos_delta) + .until(|block: &Block| block.is_filled()) + .ignore_error() + .cast(); + + pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist; + + // Can't fail since we do ignore_error above + if block.unwrap().is_some() { + let block_center = pos.0.map(|e| e.floor()) + 0.5; + let block_rpos = (pos.0 - block_center) + .try_normalized() + .unwrap_or(Vec3::zero()); + + // See whether we're on the top/bottom of a block, or the side + if block_rpos.z.abs() + > block_rpos.xy().map(|e| e.abs()).reduce_partial_max() + { + if block_rpos.z > 0.0 { + physics_state.on_ground = true; + } else { + physics_state.on_ceiling = true; + } + vel.0.z = 0.0; + } else { + physics_state.on_wall = + Some(if block_rpos.x.abs() > block_rpos.y.abs() { + vel.0.x = 0.0; + Vec3::unit_x() * -block_rpos.x.signum() + } else { + vel.0.y = 0.0; + Vec3::unit_y() * -block_rpos.y.signum() + }); + } } - if let Collider::Voxel { id } = collider_other { - // use bounding cylinder regardless of our collider - // TODO: extract point-terrain collision above to its own function - let radius = collider.get_radius() * scale; - let (z_min, z_max) = collider.get_z_limits(scale); + physics_state.in_liquid = psdr + .terrain + .get(pos.0.map(|e| e.floor() as i32)) + .ok() + .and_then(|vox| vox.is_liquid().then_some(1.0)); + }, + } - let cylinder = (radius, z_min, z_max); - // TODO: load .vox into a Dyna, and use it (appropriately rotated) - // as the terrain - /*cylinder_voxel_collision( + // Collide with terrain-like entities + for ( + entity_other, + other, + pos_other, + previous_cache_other, + mass_other, + collider_other, + _, + _, + _, + _, + char_state_other_maybe, + ) in ( + &psdr.entities, + &psdr.uids, + positions, + previous_phys_cache, + psdr.masses.maybe(), + &psdr.colliders, + !&psdr.projectiles, + !&psdr.mountings, + !&psdr.beams, + !&psdr.shockwaves, + psdr.char_states.maybe(), + ) + .join() + { + /*let collision_boundary = previous_cache.collision_boundary + + previous_cache_other.collision_boundary; + if previous_cache + .center + .distance_squared(previous_cache_other.center) + > collision_boundary.powi(2) + || entity == entity_other + { + continue; + }*/ + + if let Collider::Voxel { id } = collider_other { + // use bounding cylinder regardless of our collider + // TODO: extract point-terrain collision above to its own function + let radius = collider.get_radius() * scale; + let (z_min, z_max) = collider.get_z_limits(scale); + + pos.0 -= pos_other.0; + let cylinder = (radius, z_min, z_max); + if let Some(dyna) = VOXEL_COLLIDER_MANIFEST.voxes.get(id) { + cylinder_voxel_collision( cylinder, - &*psdr.terrain, + &*dyna, entity, &mut pos, pos_delta, vel, &mut physics_state, &mut land_on_grounds, - );*/ + ); } + pos.0 += pos_other.0; } - if pos != old_pos { - pos_writes.push((entity, pos)); - } + } + if pos != old_pos { + pos_writes.push((entity, pos)); + } - (pos_writes, land_on_grounds) - }, - ) - .reduce( - || (Vec::new(), Vec::new()), - |(mut pos_writes_a, mut land_on_grounds_a), - (mut pos_writes_b, mut land_on_grounds_b)| { - pos_writes_a.append(&mut pos_writes_b); - land_on_grounds_a.append(&mut land_on_grounds_b); - (pos_writes_a, land_on_grounds_a) - }, - ); + (pos_writes, land_on_grounds) + }, + ) + .reduce( + || (Vec::new(), Vec::new()), + |(mut pos_writes_a, mut land_on_grounds_a), + (mut pos_writes_b, mut land_on_grounds_b)| { + pos_writes_a.append(&mut pos_writes_b); + land_on_grounds_a.append(&mut land_on_grounds_b); + (pos_writes_a, land_on_grounds_a) + }, + ); drop(guard); job.cpu_stats.measure(ParMode::Single); @@ -631,10 +654,7 @@ impl<'a> System<'a> for Sys { #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 #[allow(clippy::blocks_in_if_conditions)] // TODO: Pending review in #587 - fn run( - job: &mut Job, - mut psd: Self::SystemData, - ) { + fn run(job: &mut Job, mut psd: Self::SystemData) { psd.reset(); // Apply pushback @@ -653,7 +673,6 @@ impl<'a> System<'a> for Sys { psd.maintain_pushback_cache(); psd.apply_pushback(job); - psd.handle_movement_and_terrain(job); } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7e6dc4c454..359bc42413 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -993,11 +993,12 @@ fn handle_spawn_airship( _action: &ChatCommand, ) { match server.state.read_component_copied::(target) { - Some(pos) => { + Some(mut pos) => { + pos.0.z += 50.0; server .state - .create_ship(pos, comp::ship::Body::DefaultAirship) - .with(comp::Scale(50.0)) + .create_ship(pos, comp::ship::Body::DefaultAirship, 1) + .with(comp::Scale(11.0)) .with(LightEmitter { col: Rgb::new(1.0, 0.65, 0.2), strength: 2.0, diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 770b6d28b0..0fdb8c124f 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -41,7 +41,7 @@ pub trait StateExt { ) -> EcsEntityBuilder; /// Build a static object entity fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder; - fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body) -> EcsEntityBuilder; + fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body, level: u16) -> EcsEntityBuilder; /// Build a projectile fn create_projectile( &mut self, @@ -216,16 +216,24 @@ impl StateExt for State { .with(comp::Gravity(1.0)) } - fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body) -> EcsEntityBuilder { + fn create_ship(&mut self, pos: comp::Pos, ship: comp::ship::Body, level: u16) -> EcsEntityBuilder { self.ecs_mut() .create_entity_synced() .with(pos) .with(comp::Vel(Vec3::zero())) .with(comp::Ori::default()) .with(comp::Mass(50.0)) - .with(comp::Collider::Voxel { id: object.manifest_id().to_string() }) - .with(comp::Body::Ship(object)) + .with(comp::Collider::Voxel { id: ship.manifest_entry().to_string() }) + .with(comp::Body::Ship(ship)) .with(comp::Gravity(1.0)) + .with(comp::Controller::default()) + .with(comp::inventory::Inventory::new_empty()) + .with(comp::CharacterState::default()) + .with(comp::Energy::new(ship.into(), level)) + .with(comp::Health::new(ship.into(), level)) + .with(comp::Stats::new("Airship".to_string())) + .with(comp::Buffs::default()) + .with(comp::Combo::default()) } fn create_projectile( diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 12281f4edf..2bba21b833 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -4231,7 +4231,7 @@ impl FigureMgr { .join() // Don't render dead entities .filter(|(_, _, _, _, health, _, _)| health.map_or(true, |h| !h.is_dead)) - .for_each(|(entity, pos, _, body, _, inventory, _)| { + .for_each(|(entity, pos, _, body, _, inventory, scale)| { if let Some((locals, bone_consts, model, _)) = self.get_model_for_render( tick, camera, @@ -4241,7 +4241,7 @@ impl FigureMgr { inventory, false, pos.0, - figure_lod_render_distance, + figure_lod_render_distance * scale.map_or(1.0, |s| s.0), |state| state.can_shadow_sun(), ) { renderer.render_figure_shadow_directed( @@ -4273,7 +4273,7 @@ impl FigureMgr { let character_state_storage = state.read_storage::(); let character_state = character_state_storage.get(player_entity); - for (entity, pos, _, body, _, inventory, _) in ( + for (entity, pos, _, body, _, inventory, scale) in ( &ecs.entities(), &ecs.read_storage::(), ecs.read_storage::().maybe(), @@ -4298,7 +4298,7 @@ impl FigureMgr { inventory, false, pos.0, - figure_lod_render_distance, + figure_lod_render_distance * scale.map_or(1.0, |s| s.0), |state| state.visible(), ) { renderer.render_figure(model, &col_lights, global, locals, bone_consts, lod);