diff --git a/CHANGELOG.md b/CHANGELOG.md index 817b10df57..89d4b586e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Colors for models and figures were adjusted to account for the saturation hack. - Fixed a bug where the closest item would be picked up instead of a selected item. - Fixed a bug where camera zoom in and zoom out distance didn't match. +- Overhauled world colours +- Improved projectile physics +- Improved overhead aiming ### Removed diff --git a/assets/voxygen/shaders/lod-terrain-frag.glsl b/assets/voxygen/shaders/lod-terrain-frag.glsl index 8ae0f0ab2c..1625f902c4 100644 --- a/assets/voxygen/shaders/lod-terrain-frag.glsl +++ b/assets/voxygen/shaders/lod-terrain-frag.glsl @@ -25,6 +25,7 @@ in vec3 f_pos; in vec3 f_norm; +in float pull_down; // in vec2 v_pos_orig; // in vec4 f_shadow; // in vec4 f_square; @@ -106,7 +107,7 @@ void main() { // mat4 invfoo = foo * inverse(foo * all_mat); // vec3 old_coord = all_mat * vec4(f_pos.xyz, 1.0); // vec4 new_f_pos = invfoo * (old_coord);//vec4(f_pos, 1.0); - vec3 f_col = lod_col(f_pos.xy); + vec3 f_col = mix(lod_col(f_pos.xy), vec3(0), clamp(pull_down / 10, 0, 1)); // tgt_color = vec4(f_col, 1.0); // return; // vec3 f_col = srgb_to_linear(vec3(1.0)); diff --git a/assets/voxygen/shaders/lod-terrain-vert.glsl b/assets/voxygen/shaders/lod-terrain-vert.glsl index 6ef2dd7bdf..2e9d38da4d 100644 --- a/assets/voxygen/shaders/lod-terrain-vert.glsl +++ b/assets/voxygen/shaders/lod-terrain-vert.glsl @@ -29,6 +29,7 @@ uniform u_locals { out vec3 f_pos; out vec3 f_norm; +out float pull_down; // out vec2 v_pos_orig; // out vec4 f_square; // out vec4 f_shadow; @@ -48,7 +49,8 @@ void main() { // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); - f_pos.z -= 1.0 / pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0); + pull_down = 1.0 / pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0); + f_pos.z -= pull_down; // f_pos.z -= 100.0 * pow(1.0 + 0.01 / view_distance.x, -pow(distance(focus_pos.xy, f_pos.xy), 2.0)); // f_pos.z = mix(-f_pos.z, f_pos.z, view_distance.x <= distance(focus_pos.xy, f_pos.xy) + 32.0); diff --git a/assets/voxygen/shaders/terrain-frag.glsl b/assets/voxygen/shaders/terrain-frag.glsl index efbc5d4125..dd4033ae29 100644 --- a/assets/voxygen/shaders/terrain-frag.glsl +++ b/assets/voxygen/shaders/terrain-frag.glsl @@ -345,7 +345,7 @@ void main() { const float A = 0.055; const float W_INV = 1 / (1 + A); const float W_2 = W_INV * W_INV;//pow(W_INV, 2.4); - const float NOISE_FACTOR = 0.02;//pow(0.02, 1.2); + const float NOISE_FACTOR = 0.015;//pow(0.02, 1.2); vec3 noise_delta = (sqrt(f_col) * W_INV + noise * NOISE_FACTOR); // noise_delta = noise_delta * noise_delta * W_2 - f_col; // lum = W ⋅ col diff --git a/assets/world/style/colors.ron b/assets/world/style/colors.ron index bc03c21078..6f234e82d0 100644 --- a/assets/world/style/colors.ron +++ b/assets/world/style/colors.ron @@ -17,13 +17,13 @@ Water: None, GreenSludge: None, // Leaves all actually get interpolated. - TemperateLeaves: (start: (0, 132, 94), end: (142, 181, 0)), - PineLeaves: (start: (0, 60, 50), end: (30, 100, 10)), - PalmLeavesInner: (start: (61, 166, 43), end: (29, 130, 32)), - PalmLeavesOuter: (start: (62, 171, 38), end: (45, 171, 65)), - Acacia: (start: (15, 126, 50), end: (30, 180, 10)), + TemperateLeaves: (start: (0, 70, 45), end: (90, 140, 0)), + PineLeaves: (start: (0, 60, 50), end: (30, 80, 10)), + PalmLeavesInner: (start: (70, 140, 43), end: (55, 140, 32)), + PalmLeavesOuter: (start: (60, 130, 38), end: (30, 130, 65)), + Acacia: (start: (30, 100, 0), end: (90, 110, 20)), Liana: (start: (0, 125, 107), end: (0, 155, 129)), - Mangrove: (start: (32, 56, 22), end: (57, 69, 27)), + Mangrove: (start: (15, 80, 10), end: (20, 120, 47)), ) // Water blocks ignore color now so this isn't used, but just in case this color was worth @@ -31,34 +31,35 @@ // green_sludge: (30.0, 126.0, 23.0) ), column: ( - cold_grass: (0.0, 0.5, 0.25), - warm_grass: (0.4, 0.8, 0.0), + cold_grass: (0.0, 0.3, 0.1), + warm_grass: (0.5, 0.55, 0.0), dark_grass: (0.15, 0.4, 0.1), wet_grass: (0.1, 0.8, 0.2), - cold_stone: (0.57, 0.67, 0.8), - hot_stone: (0.07, 0.07, 0.06), - warm_stone: (0.77, 0.77, 0.64), + cold_stone: (0.4, 0.67, 0.8), + hot_stone: (0.05, 0.05, 0.04), + warm_stone: (0.30, 0.2, 0.15), beach_sand: (0.8, 0.75, 0.5), - desert_sand: (0.7, 0.4, 0.25), - snow: (0.8, 0.85, 1.0), + desert_sand: (0.6, 0.4, 0.2), + snow: (0.75, 0.8, 1.8), + snow_moss: (0.35, 0.55, 0.7), - stone_col: (195, 187, 201), + stone_col: (90, 110, 150), dirt_low: (0.075, 0.07, 0.3), - dirt_high: (0.75, 0.55, 0.1), + dirt_high: (0.6, 0.3, 0.05), snow_high: (0.01, 0.3, 0.0), - warm_stone_high: (0.3, 0.12, 0.2), + warm_stone_high: (0.25, 0.22, 0.3), grass_high: (0.15, 0.2, 0.15), - tropical_high: (0.87, 0.62, 0.56), + tropical_high: (0.95, 0.55, 0.50), ), // NOTE: I think (but am not sure) that this is the color of stuff below the bottom-most // ground. I'm not sure how easy it is to see. deep_stone_color: (125, 120, 130), layer: ( bridge: (80, 80, 100), - stalagtite: (200, 200, 200), + stalagtite: (140, 150, 200), ), site: ( castle: (), @@ -74,24 +75,25 @@ pole: (90, 70, 50), flag: ( Evil: (80, 10, 130), - Good: (200, 80, 40), + Good: (150, 20, 0), ), stone: ( Evil: (65, 60, 55), - Good: (100, 100, 110), + Good: (70, 75, 80), ), ), house: ( - foundation: (100, 100, 100), + foundation: (70, 70, 70), floor: (100, 75, 50), roof: ( Roof1: (0x99, 0x5E, 0x54), Roof2: (0x43, 0x63, 0x64), Roof3: (0x76, 0x6D, 0x68), - Roof4: (0x7B, 0x41, 0x61), + Roof4: (0x55, 0x25, 0x41), Roof5: (0x52, 0x20, 0x20), - Roof6: (0x1A, 0x4A, 0x59), - Roof7: (0xCC, 0x76, 0x4E), + Roof6: (0x05, 0x3A, 0x40), + Roof7: (0xCC, 0x56, 0x3E), + Roof8: (0x05, 0x48, 0x40), // (0x1D, 0x4D, 0x45), // (0xB3, 0x7D, 0x60), // (0xAC, 0x5D, 0x26), @@ -122,18 +124,18 @@ Wall9: (0xAE, 0x8D, 0x9C), ), support: ( - Support1: (60, 45, 30), - Support2: (0x65, 0x55, 0x56), + Support1: (65, 30, 0), + Support2: (0x35, 0x25, 0x26), Support3: (0x53, 0x33, 0x13), - Support4: (0x58, 0x42, 0x33), + Support4: (0x65, 0x30, 0), ), ), ), ), - plot_town_path: (100, 95, 65), + plot_town_path: (90, 70, 45), - plot_field_dirt: (80, 55, 35), - plot_field_mound: (70, 80, 30), + plot_field_dirt: (55, 20, 5), + plot_field_mound: (40, 60, 10), wall_low: (130, 100, 0), wall_high :(90, 70, 50), diff --git a/assets/world/tree/mangroves/1.vox b/assets/world/tree/mangroves/1.vox index 9f2aa6eebb..ef6d466b78 100644 Binary files a/assets/world/tree/mangroves/1.vox and b/assets/world/tree/mangroves/1.vox differ diff --git a/assets/world/tree/mangroves/2.vox b/assets/world/tree/mangroves/2.vox index acae0739b7..aa6c3e57be 100644 Binary files a/assets/world/tree/mangroves/2.vox and b/assets/world/tree/mangroves/2.vox differ diff --git a/assets/world/tree/mangroves/3.vox b/assets/world/tree/mangroves/3.vox index c6651c3418..8e3eb2d0c8 100644 Binary files a/assets/world/tree/mangroves/3.vox and b/assets/world/tree/mangroves/3.vox differ diff --git a/assets/world/tree/mangroves/4.vox b/assets/world/tree/mangroves/4.vox index f6494db125..926f607cae 100644 Binary files a/assets/world/tree/mangroves/4.vox and b/assets/world/tree/mangroves/4.vox differ diff --git a/assets/world/tree/mangroves/5.vox b/assets/world/tree/mangroves/5.vox index cd006f9e0d..7098a2afc8 100644 Binary files a/assets/world/tree/mangroves/5.vox and b/assets/world/tree/mangroves/5.vox differ diff --git a/assets/world/tree/mangroves/6.vox b/assets/world/tree/mangroves/6.vox index e479f7bddb..56b4231490 100644 Binary files a/assets/world/tree/mangroves/6.vox and b/assets/world/tree/mangroves/6.vox differ diff --git a/assets/world/tree/mangroves/7.vox b/assets/world/tree/mangroves/7.vox index 23bdfeb10a..5eda851bf2 100644 Binary files a/assets/world/tree/mangroves/7.vox and b/assets/world/tree/mangroves/7.vox differ diff --git a/assets/world/tree/mangroves/8.vox b/assets/world/tree/mangroves/8.vox index bd41c78916..a5d61c9d58 100644 Binary files a/assets/world/tree/mangroves/8.vox and b/assets/world/tree/mangroves/8.vox differ diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 7d29f69412..ef0496043f 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -139,43 +139,76 @@ impl Body { pub fn radius(&self) -> f32 { // TODO: Improve these values (some might be reliant on more info in inner type) match self { - Body::Humanoid(_) => 0.2, - Body::QuadrupedSmall(_) => 0.3, - Body::QuadrupedMedium(_) => 0.9, - Body::Critter(_) => 0.2, - Body::BirdMedium(_) => 0.5, - Body::FishMedium(_) => 0.5, - Body::Dragon(_) => 2.5, - Body::BirdSmall(_) => 0.2, - Body::FishSmall(_) => 0.2, - Body::BipedLarge(_) => 3.0, - Body::Golem(_) => 2.5, - Body::QuadrupedLow(_) => 1.0, - Body::Object(_) => 0.3, + Body::Humanoid(_) => 0.35, + Body::QuadrupedSmall(_) => 0.4, + Body::QuadrupedMedium(body) => match body.species { + quadruped_medium::Species::Grolgar => 1.9, + quadruped_medium::Species::Tarasque => 2.2, + quadruped_medium::Species::Lion => 1.9, + quadruped_medium::Species::Saber => 1.8, + quadruped_medium::Species::Catoblepas => 1.7, + _ => 1.5, + }, + Body::QuadrupedLow(body) => match body.species { + quadruped_low::Species::Asp => 1.8, + quadruped_low::Species::Monitor => 1.75, + quadruped_low::Species::Crocodile => 2.1, + quadruped_low::Species::Salamander => 1.9, + quadruped_low::Species::Pangolin => 1.3, + _ => 1.6, + }, + Body::Critter(_) => 0.3, + Body::BirdMedium(_) => 0.35, + Body::FishMedium(_) => 0.35, + Body::Dragon(_) => 8.0, + Body::BirdSmall(_) => 0.3, + Body::FishSmall(_) => 0.3, + Body::BipedLarge(_) => 0.75, + Body::Golem(_) => 0.4, + Body::Object(_) => 0.4, } } pub fn height(&self) -> f32 { match self { Body::Humanoid(humanoid) => match humanoid.species { - humanoid::Species::Danari => 0.8, - humanoid::Species::Dwarf => 0.9, - humanoid::Species::Orc => 1.14, - humanoid::Species::Undead => 0.95, + humanoid::Species::Danari => 1.5, + humanoid::Species::Dwarf => 1.55, + humanoid::Species::Orc => 1.95, + _ => 1.8, + }, + Body::QuadrupedSmall(body) => match body.species { + quadruped_small::Species::Dodarock => 1.5, + quadruped_small::Species::Holladon => 1.5, + quadruped_small::Species::Truffler => 2.0, _ => 1.0, }, - Body::QuadrupedSmall(_) => 0.6, - Body::QuadrupedMedium(_) => 0.5, - Body::Critter(_) => 0.4, - Body::BirdMedium(_) => 1.2, - Body::FishMedium(_) => 1.0, - Body::Dragon(_) => 5.0, - Body::BirdSmall(_) => 0.4, - Body::FishSmall(_) => 0.4, - Body::BipedLarge(_) => 5.0, - Body::Golem(_) => 5.0, - Body::QuadrupedLow(_) => 0.5, - Body::Object(_) => 0.6, + Body::QuadrupedMedium(body) => match body.species { + quadruped_medium::Species::Tarasque => 2.5, + quadruped_medium::Species::Lion => 1.8, + quadruped_medium::Species::Saber => 1.8, + quadruped_medium::Species::Catoblepas => 2.8, + _ => 1.6, + }, + Body::QuadrupedLow(body) => match body.species { + quadruped_low::Species::Monitor => 1.5, + quadruped_low::Species::Tortoise => 2.0, + quadruped_low::Species::Rocksnapper => 2.0, + quadruped_low::Species::Maneater => 4.0, + _ => 1.3, + }, + Body::Critter(_) => 0.7, + Body::BirdMedium(body) => match body.species { + bird_medium::Species::Cockatrice => 1.8, + _ => 1.1, + }, + Body::FishMedium(_) => 1.1, + Body::Dragon(_) => 20.0, + Body::BirdSmall(_) => 1.1, + Body::FishSmall(_) => 0.9, + Body::BipedLarge(_) => 4.5, + Body::Golem(_) => 5.8, + Body::Object(_) => 1.0, } } diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 4e38c59ef0..88e4a64032 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -55,6 +55,22 @@ pub enum Collider { Point, } +impl Collider { + pub fn get_radius(&self) -> f32 { + match self { + Collider::Box { radius, .. } => *radius, + Collider::Point => 0.0, + } + } + + pub fn get_z_limits(&self) -> (f32, f32) { + match self { + Collider::Box { z_min, z_max, .. } => (*z_min, *z_max), + Collider::Point => (0.0, 0.0), + } + } +} + impl Component for Collider { type Storage = FlaggedStorage>; } @@ -74,16 +90,26 @@ impl Component for Sticky { } // PhysicsState -#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct PhysicsState { pub on_ground: bool, pub on_ceiling: bool, pub on_wall: Option>, - pub touch_entity: Option, + pub touch_entities: Vec, pub in_fluid: Option, // Depth } impl PhysicsState { + pub fn reset(&mut self) { + // Avoid allocation overhead! + let mut touch_entities = std::mem::take(&mut self.touch_entities); + touch_entities.clear(); + *self = Self { + touch_entities, + ..Self::default() + } + } + pub fn on_surface(&self) -> Option> { self.on_ground .then_some(-Vec3::unit_z()) diff --git a/common/src/path.rs b/common/src/path.rs index 78f0d76e51..cf02e684ae 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -418,7 +418,7 @@ impl Chaser { }); if !walking_towards_edge { - Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 0.75)) + Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 1.0)) } else { None } diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index c8130c1bb5..239d1fb2c1 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -33,7 +33,7 @@ impl CharacterBehavior for Data { } } - if self.buildup_duration != Duration::default() && data.physics.touch_entity.is_none() { + if self.buildup_duration != Duration::default() && data.physics.touch_entities.is_empty() { // Build up (this will move you forward) update.vel.0 = Vec3::new(0.0, 0.0, update.vel.0.z) + (update.vel.0 * Vec3::new(1.0, 1.0, 0.0) diff --git a/common/src/states/triple_strike.rs b/common/src/states/triple_strike.rs index 2a9c76b679..127e83abfd 100644 --- a/common/src/states/triple_strike.rs +++ b/common/src/states/triple_strike.rs @@ -113,7 +113,7 @@ impl CharacterBehavior for Data { // Handling movement if stage_time_active < Duration::from_millis(STAGE_DURATION / 3) { - let adjusted_accel = match (self.stage, data.physics.touch_entity.is_none()) { + let adjusted_accel = match (self.stage, data.physics.touch_entities.is_empty()) { (Stage::First, true) => INITIAL_ACCEL, (Stage::Second, true) => INITIAL_ACCEL * 0.75, (Stage::Third, true) => INITIAL_ACCEL * 0.75, diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 10b0e503d6..9294c6c869 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -96,20 +96,153 @@ impl<'a> System<'a> for Sys { ) { let mut event_emitter = event_bus.emitter(); - // Add physics state components - for entity in ( + // Add/reset physics state components + for (entity, _, _, _, _) in ( &entities, - !&physics_states, &colliders, &positions, &velocities, &orientations, ) .join() - .map(|(e, _, _, _, _, _)| e) - .collect::>() { - let _ = physics_states.insert(entity, Default::default()); + let _ = physics_states + .entry(entity) + .map(|e| e.or_insert_with(Default::default)); + } + + // Apply pushback + // + // Note: We now do this first because we project velocity ahead. This is slighty + // imperfect and implies that we might get edge-cases where entities + // standing right next to the edge of a wall may get hit by projectiles + // fired into the wall very close to them. However, this sort of thing is + // already possible with poorly-defined hitboxes anyway so it's not too + // much of a concern. + // + // If this situation becomes a problem, this code should be integrated with the + // terrain collision code below, although that's not trivial to do since + // it means the step needs to take into account the speeds of both + // entities. + for (entity, pos, scale, mass, collider, _, _, physics, projectile) in ( + &entities, + &positions, + scales.maybe(), + masses.maybe(), + colliders.maybe(), + !&mountings, + stickies.maybe(), + &mut physics_states, + // TODO: if we need to avoid collisions for other things consider moving whether it + // should interact into the collider component or into a separate component + projectiles.maybe(), + ) + .join() + .filter(|(_, _, _, _, _, _, sticky, physics, _)| { + sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground) + }) + { + let scale = scale.map(|s| s.0).unwrap_or(1.0); + let radius = collider.map(|c| c.get_radius()).unwrap_or(0.5); + let z_limits = collider.map(|c| c.get_z_limits()).unwrap_or((-0.5, 0.5)); + let mass = mass.map(|m| m.0).unwrap_or(scale); + + // Group to ignore collisions with + let ignore_group = projectile + .and_then(|p| p.owner) + .and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into())) + .and_then(|e| groups.get(e)); + + let mut vel_delta = Vec3::zero(); + + for ( + entity_other, + other, + pos_other, + scale_other, + mass_other, + collider_other, + _, + group, + ) in ( + &entities, + &uids, + &positions, + scales.maybe(), + masses.maybe(), + colliders.maybe(), + !&mountings, + groups.maybe(), + ) + .join() + { + if ignore_group.is_some() && ignore_group == group { + continue; + } + + let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0); + let radius_other = collider_other.map(|c| c.get_radius()).unwrap_or(0.5); + let z_limits_other = collider_other + .map(|c| c.get_z_limits()) + .unwrap_or((-0.5, 0.5)); + let mass_other = mass_other.map(|m| m.0).unwrap_or(scale_other); + if mass_other == 0.0 { + continue; + } + + let collision_dist = scale * radius + scale_other * radius_other; + + let vel = velocities.get(entity).copied().unwrap_or_default().0; + let vel_other = velocities.get(entity_other).copied().unwrap_or_default().0; + + // Sanity check: don't try colliding entities that are too far from each other + // Note: I think this catches all cases. If you get entity collision problems, + // try removing this! + if (pos.0 - pos_other.0).xy().magnitude() + > ((vel - vel_other) * dt.0).xy().magnitude() + collision_dist + { + continue; + } + + let min_collision_dist = 0.3; + let increments = ((vel - vel_other).magnitude() * dt.0 / min_collision_dist) + .max(1.0) + .ceil() as usize; + let step_delta = 1.0 / increments as f32; + let mut collided = false; + for i in 0..increments { + let factor = i as f32 * step_delta; + let pos = pos.0 + vel * dt.0 * factor; + let pos_other = pos_other.0 + vel_other * dt.0 * factor; + + let diff = pos.xy() - pos_other.xy(); + + if diff.magnitude_squared() <= collision_dist.powf(2.0) + && pos.z + z_limits.1 * scale + >= pos_other.z + z_limits_other.0 * scale_other + && pos.z + z_limits.0 * scale + <= pos_other.z + z_limits_other.1 * scale_other + { + if !collided { + physics.touch_entities.push(*other); + } + + if diff.magnitude_squared() > 0.0 { + let force = 40.0 * (collision_dist - diff.magnitude()) * mass_other + / (mass + mass_other); + + vel_delta += Vec3::from(diff.normalized()) * force * step_delta; + } + + collided = true; + } + } + } + + // Change velocity + velocities + .get_mut(entity) + .map(|vel| vel.0 += vel_delta * dt.0); } // Apply movement inputs @@ -177,16 +310,18 @@ impl<'a> System<'a> for Sys { Vec3::zero() }; - match collider { + match *collider { Collider::Box { radius, z_min, z_max, } => { // Scale collider - let radius = *radius; // * scale; - let z_min = *z_min; // * scale; - let z_max = *z_max; // * scale; + // TODO: Use scale & actual proportions when pathfinding is good enough to manage irregular entity + // sizes + let radius = radius.min(0.45); // * scale; + let z_min = z_min; // * scale; + let z_max = z_max.clamped(1.2, 1.95); // * scale; // Probe distances let hdist = radius.ceil() as i32; @@ -515,74 +650,5 @@ impl<'a> System<'a> for Sys { land_on_grounds.into_iter().for_each(|(entity, vel)| { event_emitter.emit(ServerEvent::LandOnGround { entity, vel: vel.0 }); }); - - // Apply pushback - for (pos, scale, mass, vel, _, _, _, physics, projectile) in ( - &positions, - scales.maybe(), - masses.maybe(), - &mut velocities, - &colliders, - !&mountings, - stickies.maybe(), - &mut physics_states, - // TODO: if we need to avoid collisions for other things consider moving whether it - // should interact into the collider component or into a separate component - projectiles.maybe(), - ) - .join() - .filter(|(_, _, _, _, _, _, sticky, physics, _)| { - sticky.is_none() || (physics.on_wall.is_none() && !physics.on_ground) - }) - { - physics.touch_entity = None; - - let scale = scale.map(|s| s.0).unwrap_or(1.0); - let mass = mass.map(|m| m.0).unwrap_or(scale); - - // Group to ignore collisions with - let ignore_group = projectile - .and_then(|p| p.owner) - .and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into())) - .and_then(|e| groups.get(e)); - - for (other, pos_other, scale_other, mass_other, _, _, group) in ( - &uids, - &positions, - scales.maybe(), - masses.maybe(), - &colliders, - !&mountings, - groups.maybe(), - ) - .join() - { - if ignore_group.is_some() && ignore_group == group { - continue; - } - - let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0); - - let mass_other = mass_other.map(|m| m.0).unwrap_or(scale_other); - if mass_other == 0.0 { - continue; - } - - let diff = Vec2::::from(pos.0 - pos_other.0); - - let collision_dist = 0.55 * (scale + scale_other); - - if diff.magnitude_squared() > 0.0 - && diff.magnitude_squared() < collision_dist.powf(2.0) - && pos.0.z + 1.6 * scale > pos_other.0.z - && pos.0.z < pos_other.0.z + 1.6 * scale_other - { - let force = (collision_dist - diff.magnitude()) * 2.0 * mass_other - / (mass + mass_other); - vel.0 += Vec3::from(diff.normalized()) * force; - physics.touch_entity = Some(*other); - } - } - } } } diff --git a/common/src/sys/projectile.rs b/common/src/sys/projectile.rs index 439f038a95..44dbf39a73 100644 --- a/common/src/sys/projectile.rs +++ b/common/src/sys/projectile.rs @@ -61,30 +61,13 @@ impl<'a> System<'a> for Sys { ) .join() { - // Hit something solid - if physics.on_wall.is_some() || physics.on_ground || physics.on_ceiling { - for effect in projectile.hit_solid.drain(..) { - match effect { - projectile::Effect::Explode { power } => { - server_emitter.emit(ServerEvent::Explosion { - pos: pos.0, - power, - owner: projectile.owner, - friendly_damage: false, - reagent: None, - }) - }, - projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy { - entity, - cause: HealthSource::World, - }), - _ => {}, - } - } - } // Hit entity - else if let Some(other) = physics.touch_entity { - for effect in projectile.hit_entity.drain(..) { + for other in physics.touch_entities.iter().copied() { + if projectile.owner == Some(other) { + continue; + } + + for effect in projectile.hit_entity.iter().cloned() { match effect { projectile::Effect::Damage(healthchange) => { let owner_uid = projectile.owner.unwrap(); @@ -151,7 +134,31 @@ impl<'a> System<'a> for Sys { _ => {}, } } - } else if let Some(dir) = velocities + } + + // Hit something solid + if physics.on_wall.is_some() || physics.on_ground || physics.on_ceiling { + for effect in projectile.hit_solid.drain(..) { + match effect { + projectile::Effect::Explode { power } => { + server_emitter.emit(ServerEvent::Explosion { + pos: pos.0, + power, + owner: projectile.owner, + friendly_damage: false, + reagent: None, + }) + }, + projectile::Effect::Vanish => server_emitter.emit(ServerEvent::Destroy { + entity, + cause: HealthSource::World, + }), + _ => {}, + } + } + } + + if let Some(dir) = velocities .get(entity) .and_then(|vel| vel.0.try_normalized()) { diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index ac5b8e098f..3b0a4afcbe 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -102,20 +102,15 @@ impl StateExt for State { .with(comp::Vel(Vec3::zero())) .with(comp::Ori::default()) .with(comp::Collider::Box { - radius: 0.4, + radius: body.radius(), z_min: 0.0, - z_max: 1.75, + z_max: body.height(), }) .with(comp::Controller::default()) .with(body) .with(stats) .with(comp::Alignment::Npc) .with(comp::Energy::new(500)) - .with(comp::Collider::Box { - radius: 0.4, - z_min: 0.0, - z_max: 1.75, - }) .with(comp::Gravity(1.0)) .with(comp::CharacterState::default()) .with(loadout) @@ -127,13 +122,13 @@ impl StateExt for State { .with(pos) .with(comp::Vel(Vec3::zero())) .with(comp::Ori::default()) - .with(comp::Body::Object(object)) .with(comp::Mass(5.0)) .with(comp::Collider::Box { - radius: 0.4, + radius: comp::Body::Object(object).radius(), z_min: 0.0, - z_max: 0.9, + z_max: comp::Body::Object(object).height(), }) + .with(comp::Body::Object(object)) .with(comp::Gravity(1.0)) } @@ -223,6 +218,11 @@ impl StateExt for State { }), )); + self.write_component(entity, comp::Collider::Box { + radius: body.radius(), + z_min: 0.0, + z_max: body.height(), + }); self.write_component(entity, body); self.write_component(entity, stats); self.write_component(entity, inventory); diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index 6a004489d3..40ef0d5edb 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -82,10 +82,7 @@ fn maps_idle() { &CharacterState::Idle {}, &PhysicsState { on_ground: true, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, + ..Default::default() }, &PreviousEntityState { event: SfxEvent::Idle, @@ -104,10 +101,7 @@ fn maps_run_with_sufficient_velocity() { &CharacterState::Idle {}, &PhysicsState { on_ground: true, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, + ..Default::default() }, &PreviousEntityState { event: SfxEvent::Idle, @@ -126,10 +120,7 @@ fn does_not_map_run_with_insufficient_velocity() { &CharacterState::Idle {}, &PhysicsState { on_ground: true, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, + ..Default::default() }, &PreviousEntityState { event: SfxEvent::Idle, @@ -146,13 +137,7 @@ fn does_not_map_run_with_insufficient_velocity() { fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() { let result = MovementEventMapper::map_movement_event( &CharacterState::Idle {}, - &PhysicsState { - on_ground: false, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, - }, + &Default::default(), &PreviousEntityState { event: SfxEvent::Idle, time: Instant::now(), @@ -173,10 +158,7 @@ fn maps_roll() { }), &PhysicsState { on_ground: true, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, + ..Default::default() }, &PreviousEntityState { event: SfxEvent::Run, @@ -195,10 +177,7 @@ fn maps_land_on_ground_to_run() { &CharacterState::Idle {}, &PhysicsState { on_ground: true, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, + ..Default::default() }, &PreviousEntityState { event: SfxEvent::Idle, @@ -215,13 +194,7 @@ fn maps_land_on_ground_to_run() { fn maps_glider_open() { let result = MovementEventMapper::map_movement_event( &CharacterState::Glide {}, - &PhysicsState { - on_ground: false, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, - }, + &Default::default(), &PreviousEntityState { event: SfxEvent::Jump, time: Instant::now(), @@ -237,13 +210,7 @@ fn maps_glider_open() { fn maps_glide() { let result = MovementEventMapper::map_movement_event( &CharacterState::Glide {}, - &PhysicsState { - on_ground: false, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, - }, + &Default::default(), &PreviousEntityState { event: SfxEvent::Glide, time: Instant::now(), @@ -259,13 +226,7 @@ fn maps_glide() { fn maps_glider_close_when_closing_mid_flight() { let result = MovementEventMapper::map_movement_event( &CharacterState::Idle {}, - &PhysicsState { - on_ground: false, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, - }, + &Default::default(), &PreviousEntityState { event: SfxEvent::Glide, time: Instant::now(), @@ -284,10 +245,7 @@ fn maps_glider_close_when_landing() { &CharacterState::Idle {}, &PhysicsState { on_ground: true, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, + ..Default::default() }, &PreviousEntityState { event: SfxEvent::Glide, @@ -305,10 +263,7 @@ fn maps_quadrupeds_running() { let result = MovementEventMapper::map_non_humanoid_movement_event( &PhysicsState { on_ground: true, - on_ceiling: false, - on_wall: None, - touch_entity: None, - in_fluid: None, + ..Default::default() }, Vec3::new(0.5, 0.8, 0.0), ); diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index d006fb1684..5d4d85192f 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -476,7 +476,7 @@ impl Scene { player_scale * 1.65 } }, - CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.1, + CameraMode::ThirdPerson if scene_data.is_aiming => player_scale * 2.2, CameraMode::ThirdPerson => player_scale * 1.65, CameraMode::Freefly => 0.0, }; diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index f83a6d3583..a66f73ede5 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -238,7 +238,7 @@ impl PlayState for SessionState { ( is_aiming, if is_aiming { - Vec3::unit_z() * 0.025 + Vec3::unit_z() * 0.05 } else { Vec3::zero() }, diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 8e482d4c92..8490719cf7 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -361,12 +361,14 @@ impl<'a> BlockGen<'a> { Some(Block::new( BlockKind::Rock, - stone_col - - Rgb::new( + stone_col.map2( + Rgb::new( field0.get(wpos) as u8 % 16, field1.get(wpos) as u8 % 16, field2.get(wpos) as u8 % 16, ), + |stone, x| stone.saturating_sub(x), + ), )) } else { None diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index c8e07476e1..aa1bf46225 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -38,6 +38,7 @@ pub struct Colors { pub beach_sand: (f32, f32, f32), pub desert_sand: (f32, f32, f32), pub snow: (f32, f32, f32), + pub snow_moss: (f32, f32, f32), pub stone_col: (u8, u8, u8), @@ -802,6 +803,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { beach_sand, desert_sand, snow, + snow_moss, stone_col, dirt_low, dirt_high, @@ -839,7 +841,7 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { warm_grass, marble.sub(0.5).add(1.0.sub(humidity).mul(0.5)).powf(1.5), ); - let snow_moss = Rgb::lerp(snow, cold_grass, 0.4 + marble.powf(1.5) * 0.6); + let snow_moss = Rgb::lerp(snow_moss.into(), cold_grass, 0.4 + marble.powf(1.5) * 0.6); let moss = Rgb::lerp(dark_grass, cold_grass, marble.powf(1.5)); let rainforest = Rgb::lerp(wet_grass, warm_grass, marble.powf(1.5)); let sand = Rgb::lerp(beach_sand, desert_sand, marble); diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index b018c21fb8..14d1e69469 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -312,20 +312,28 @@ pub fn apply_scatter_to<'a>( }); if let Some(bk) = bk { - let mut z = col_sample.alt as i32 - 4; - for _ in 0..8 { - if vol - .get(Vec3::new(offs.x, offs.y, z)) - .map(|b| !b.is_solid()) - .unwrap_or(true) - { - let _ = vol.set( - Vec3::new(offs.x, offs.y, z), - Block::new(bk, Rgb::broadcast(0)), - ); - break; - } - z += 1; + let alt = col_sample.alt as i32; + + // Find the intersection between ground and air, if there is one near the + // surface + if let Some(solid_end) = (-4..8) + .find(|z| { + vol.get(Vec3::new(offs.x, offs.y, alt + z)) + .map(|b| b.is_solid()) + .unwrap_or(false) + }) + .and_then(|solid_start| { + (1..8).map(|z| solid_start + z).find(|z| { + vol.get(Vec3::new(offs.x, offs.y, alt + z)) + .map(|b| !b.is_solid()) + .unwrap_or(true) + }) + }) + { + let _ = vol.set( + Vec3::new(offs.x, offs.y, alt + solid_end), + Block::new(bk, Rgb::broadcast(0)), + ); } } }