diff --git a/client/src/lib.rs b/client/src/lib.rs index 0901a8fd27..e74be1610e 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -396,7 +396,7 @@ impl Client { } pub fn pick_up(&mut self, entity: EcsEntity) { - if let Some(uid) = self.state.read_component_copied(entity) { + if let Some(uid) = self.state.read_component_cloned(entity) { self.singleton_stream .send(ClientMsg::ControlEvent(ControlEvent::InventoryManip( InventoryManip::Pickup(uid), @@ -520,7 +520,7 @@ impl Client { } pub fn mount(&mut self, entity: EcsEntity) { - if let Some(uid) = self.state.read_component_copied(entity) { + if let Some(uid) = self.state.read_component_cloned(entity) { self.singleton_stream .send(ClientMsg::ControlEvent(ControlEvent::Mount(uid))) .unwrap(); @@ -1294,7 +1294,7 @@ impl Client { pub fn entity(&self) -> EcsEntity { self.entity } /// Get the player's Uid. - pub fn uid(&self) -> Option { self.state.read_component_copied(self.entity) } + pub fn uid(&self) -> Option { self.state.read_component_cloned(self.entity) } /// Get the client state pub fn get_client_state(&self) -> ClientState { self.client_state } @@ -1347,7 +1347,7 @@ impl Client { pub fn is_admin(&self) -> bool { let client_uid = self .state - .read_component_copied::(self.entity) + .read_component_cloned::(self.entity) .expect("Client doesn't have a Uid!!!"); self.player_list diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index a776eb2890..59a27fe6b0 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -1,5 +1,4 @@ use crate::{path::Chaser, sync::Uid, comp::Body}; -use serde::{Deserialize, Serialize}; use specs::{Component, Entity as EcsEntity}; use specs_idvs::IdvStorage; use vek::*; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 466734cb4c..2035666375 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -80,7 +80,7 @@ pub struct PhysicsState { pub on_ceiling: bool, pub on_wall: Option>, pub touch_entity: Option, - pub in_fluid: bool, + pub in_fluid: Option, // Depth } impl PhysicsState { diff --git a/common/src/state.rs b/common/src/state.rs index 3d003b7a4f..d53e91f964 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -199,8 +199,8 @@ impl State { } /// Read a component attributed to a particular entity. - pub fn read_component_copied(&self, entity: EcsEntity) -> Option { - self.ecs.read_storage().get(entity).copied() + pub fn read_component_cloned(&self, entity: EcsEntity) -> Option { + self.ecs.read_storage().get(entity).cloned() } /// Get a read-only reference to the storage of a particular component type. diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 93449a2ad3..e45b56e88e 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -24,7 +24,7 @@ impl CharacterBehavior for Data { update.character = CharacterState::GlideWield; return update; } - if data.physics.in_fluid { + if data.physics.in_fluid.map(|depth| depth > 0.5).unwrap_or(false) { update.character = CharacterState::Idle; } // If there is a wall in front of character and they are trying to climb go to diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index 0cc4d826e0..548e076155 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -19,7 +19,7 @@ impl CharacterBehavior for Data { if !data.physics.on_ground { update.character = CharacterState::Glide; } - if data.physics.in_fluid { + if data.physics.in_fluid.map(|depth| depth > 0.5).unwrap_or(false) { update.character = CharacterState::Idle; } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 7a2b4f19a1..10730d75ef 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -8,7 +8,7 @@ use crate::{ sys::{character_behavior::JoinData, phys::GRAVITY}, util::Dir, }; -use vek::vec::Vec2; +use vek::*; pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; const BASE_HUMANOID_AIR_ACCEL: f32 = 8.0; @@ -67,8 +67,8 @@ impl Body { /// Handles updating `Components` to move player based on state of `JoinData` pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { - if data.physics.in_fluid { - swim_move(data, update, efficiency); + if let Some(depth) = data.physics.in_fluid { + swim_move(data, update, efficiency, depth); } else { basic_move(data, update, efficiency); } @@ -104,7 +104,7 @@ pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, rate: f32) } /// Updates components to move player as if theyre swimming -fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { +fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, depth: f32) { // Update velocity update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir @@ -120,7 +120,7 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { // Swim if data.inputs.swimup.is_pressed() { update.vel.0.z = - (update.vel.0.z + data.dt.0 * GRAVITY * 4.0).min(BASE_HUMANOID_WATER_SPEED); + (update.vel.0.z + data.dt.0 * GRAVITY * 4.0 * depth.clamped(0.0, 1.0).powf(3.0)).min(BASE_HUMANOID_WATER_SPEED); } // Swim if data.inputs.swimdown.is_pressed() { @@ -192,14 +192,14 @@ pub fn attempt_swap_loadout(data: &JoinData, update: &mut StateUpdate) { /// Checks that player can wield the glider and updates `CharacterState` if so pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) { - if data.physics.on_ground && !data.physics.in_fluid && data.body.is_humanoid() { + if data.physics.on_ground && !data.physics.in_fluid.map(|depth| depth > 1.0).unwrap_or(false) && data.body.is_humanoid() { update.character = CharacterState::GlideWield; } } /// Checks that player can jump and sends jump event if so pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) { - if data.inputs.jump.is_pressed() && data.physics.on_ground && !data.physics.in_fluid { + if data.inputs.jump.is_pressed() && data.physics.on_ground && !data.physics.in_fluid.map(|depth| depth > 1.0).unwrap_or(false) { update .local_events .push_front(LocalEvent::Jump(data.entity)); diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 5c49227a6e..679bddb36b 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -241,7 +241,8 @@ impl<'a> System<'a> for Sys { bearing.xy().try_normalized().unwrap_or(Vec2::zero()) * speed.min(0.2 + (dist - AVG_FOLLOW_DIST) / 8.0); inputs.jump.set_state(bearing.z > 1.5); - inputs.swim.set_state(bearing.z > 0.5); + inputs.swimup.set_state(bearing.z > 0.5); + inputs.swimdown.set_state(bearing.z < 0.5); } } else { do_idle = true; @@ -331,7 +332,8 @@ impl<'a> System<'a> for Sys { .unwrap_or(Vec2::zero()) * speed; inputs.jump.set_state(bearing.z > 1.5); - inputs.swim.set_state(bearing.z > 0.5); + inputs.swimup.set_state(bearing.z > 0.5); + inputs.swimdown.set_state(bearing.z < 0.5); } } else { do_idle = true; @@ -399,7 +401,8 @@ impl<'a> System<'a> for Sys { .unwrap_or(Vec2::zero()) * speed; inputs.jump.set_state(bearing.z > 1.5); - inputs.swim.set_state(bearing.z > 0.5); + inputs.swimup.set_state(bearing.z > 0.5); + inputs.swimdown.set_state(bearing.z < 0.5); } if dist_sqrd < 16.0f32.powf(2.0) diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index b3cec5dca2..cf0caf31bb 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -13,6 +13,7 @@ use specs::{ saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage, }; use vek::*; +use std::ops::Range; pub const GRAVITY: f32 = 9.81 * 5.0; const BOUYANCY: f32 = 1.0; @@ -123,7 +124,7 @@ impl<'a> System<'a> for Sys { } else { 0.0 }) - .max(if physics_state.in_fluid { + .max(if physics_state.in_fluid.is_some() { FRIC_FLUID } else { 0.0 @@ -133,7 +134,7 @@ impl<'a> System<'a> for Sys { .is_some(); let downward_force = if !in_loaded_chunk { 0.0 // No gravity in unloaded chunks - } else if physics_state.in_fluid { + } else if physics_state.in_fluid.map(|depth| depth > 0.75).unwrap_or(false) { (1.0 - BOUYANCY) * GRAVITY } else { GRAVITY @@ -175,31 +176,47 @@ impl<'a> System<'a> for Sys { .flatten() .flatten(); - // Function for determining whether the player at a specific position collides - // with the ground - let collision_with = |pos: Vec3, - hit: &dyn Fn(&Block) -> bool, - near_iter| { - for (i, j, k) in near_iter { - let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); + // Function for iterating over the blocks the player at a specific position collides + // with + fn collision_iter<'a>( + pos: Vec3, + terrain: &'a TerrainGrid, + hit: &'a dyn Fn(&Block) -> bool, + near_iter: impl Iterator + 'a, + radius: f32, + z_range: Range, + ) -> impl Iterator> + 'a { + near_iter + .filter_map(move |(i, j, k)| { + let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); - if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) { - let player_aabb = Aabb { - min: pos + Vec3::new(-radius, -radius, z_min), - max: pos + Vec3::new(radius, radius, z_max), - }; - let block_aabb = Aabb { - min: block_pos.map(|e| e as f32), - max: block_pos.map(|e| e as f32) - + Vec3::new(1.0, 1.0, block.get_height()), - }; + if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) { + let player_aabb = Aabb { + min: pos + Vec3::new(-radius, -radius, z_range.start), + max: pos + Vec3::new(radius, radius, z_range.end), + }; + let block_aabb = Aabb { + min: block_pos.map(|e| e as f32), + max: block_pos.map(|e| e as f32) + + Vec3::new(1.0, 1.0, block.get_height()), + }; - if player_aabb.collides_with_aabb(block_aabb) { - return true; + if player_aabb.collides_with_aabb(block_aabb) { + return Some(block_aabb); + } } - } - } - false + + None + }) + }; + + // Function for determining whether the player at a specific position collides + // with blocks with the given criteria + let collision_with = |pos: Vec3, + hit: &dyn Fn(&Block) -> bool, + near_iter| + { + collision_iter(pos, &terrain, hit, near_iter, radius, z_min..z_max).count() > 0 }; let was_on_ground = physics_state.on_ground; @@ -400,8 +417,9 @@ impl<'a> System<'a> for Sys { } // Figure out if we're in water - physics_state.in_fluid = - collision_with(pos.0, &|block| block.is_fluid(), near_iter.clone()); + physics_state.in_fluid = collision_iter(pos.0, &terrain, &|block| block.is_fluid(), near_iter.clone(), radius, z_min..z_max) + .max_by_key(|block_aabb| (block_aabb.max.z * 100.0) as i32) + .map(|block_aabb| block_aabb.max.z - pos.0.z); }, Collider::Point => { let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta).ignore_error().cast(); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index a97b687c46..9abd4f49a4 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -262,7 +262,7 @@ fn handle_jump( action: &ChatCommand, ) { if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(current_pos) => { server .state @@ -287,7 +287,7 @@ fn handle_goto( if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) { if server .state - .read_component_copied::(target) + .read_component_cloned::(target) .is_some() { server @@ -498,9 +498,9 @@ fn handle_tp( ); return; }; - if let Some(_pos) = server.state.read_component_copied::(target) { + if let Some(_pos) = server.state.read_component_cloned::(target) { if let Some(player) = opt_player { - if let Some(pos) = server.state.read_component_copied::(player) { + if let Some(pos) = server.state.read_component_cloned::(player) { server.state.write_component(target, pos); server.state.write_component(target, comp::ForceUpdate); } else { @@ -545,7 +545,7 @@ fn handle_spawn( (Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount, opt_ai) => { let uid = server .state - .read_component_copied(target) + .read_component_cloned(target) .expect("Expected player to have a UID"); if let Some(alignment) = parse_alignment(uid, &opt_align) { let amount = opt_amount @@ -556,7 +556,7 @@ fn handle_spawn( let ai = opt_ai.unwrap_or_else(|| "true".to_string()); - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { let agent = if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment { @@ -666,7 +666,7 @@ fn handle_spawn_training_dummy( _args: String, _action: &ChatCommand, ) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { let vel = Vec3::new( rand::thread_rng().gen_range(-2.0, 3.0), @@ -707,7 +707,7 @@ fn handle_spawn_campfire( _args: String, _action: &ChatCommand, ) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { server .state @@ -1066,7 +1066,7 @@ fn handle_explosion( let ecs = server.state.ecs(); - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { ecs.read_resource::>() .emit_now(ServerEvent::Explosion { @@ -1090,7 +1090,7 @@ fn handle_waypoint( _args: String, _action: &ChatCommand, ) { - match server.state.read_component_copied::(target) { + match server.state.read_component_cloned::(target) { Some(pos) => { let time = server.state.ecs().read_resource(); let _ = server @@ -1126,7 +1126,7 @@ fn handle_adminify( Some(player) => { let is_admin = if server .state - .read_component_copied::(player) + .read_component_cloned::(player) .is_some() { ecs.write_storage::().remove(player); @@ -1670,7 +1670,7 @@ fn handle_remove_lights( action: &ChatCommand, ) { let opt_radius = scan_fmt_some!(&args, &action.arg_fmt(), f32); - let opt_player_pos = server.state.read_component_copied::(target); + let opt_player_pos = server.state.read_component_cloned::(target); let mut to_delete = vec![]; match opt_player_pos { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 852b8317bc..310c846308 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -251,7 +251,7 @@ pub fn handle_respawn(server: &Server, entity: EcsEntity) { .is_some() { let respawn_point = state - .read_component_copied::(entity) + .read_component_cloned::(entity) .map(|wp| wp.get_pos()) .unwrap_or(state.ecs().read_resource::().0); diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 549804d6f8..e213d312d9 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -167,10 +167,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv thrown_items.push(( *pos, state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), *kind, )); @@ -185,7 +185,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv state.read_storage::().get(entity) { let uid = state - .read_component_copied(entity) + .read_component_cloned(entity) .expect("Expected player to have a UID"); if ( &state.read_storage::(), @@ -341,7 +341,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv dropped_items.push(( *pos, state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), item, )); @@ -373,10 +373,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv for _ in 0..amount { dropped_items.push(( state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), state - .read_component_copied::(entity) + .read_component_cloned::(entity) .unwrap_or_default(), item.clone(), )); @@ -407,7 +407,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv + Vec3::unit_z() * 15.0 + Vec3::::zero().map(|_| rand::thread_rng().gen::() - 0.5) * 4.0; - let uid = state.read_component_copied::(entity); + let uid = state.read_component_cloned::(entity); let mut new_entity = state .create_object(Default::default(), match kind { diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 2c9a06e54a..439fc613d1 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -20,7 +20,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { // Note: If other `ServerEvent`s are referring to this entity they will be // disrupted let maybe_client = state.ecs().write_storage::().remove(entity); - let maybe_uid = state.read_component_copied::(entity); + let maybe_uid = state.read_component_cloned::(entity); let maybe_player = state.ecs().write_storage::().remove(entity); let maybe_group = state .ecs() diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 77b42d53a0..816b41401c 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -173,7 +173,7 @@ impl StateExt for State { self.write_component(entity, comp::CharacterState::default()); self.write_component( entity, - comp::Alignment::Owned(self.read_component_copied(entity).unwrap()), + comp::Alignment::Owned(self.read_component_cloned(entity).unwrap()), ); // Set the character id for the player @@ -213,7 +213,7 @@ impl StateExt for State { // Notify clients of a player list update let client_uid = self - .read_component_copied::(entity) + .read_component_cloned::(entity) .map(|u| u) .expect("Client doesn't have a Uid!!!"); diff --git a/voxygen/src/hud/social.rs b/voxygen/src/hud/social.rs index 78a9ef46c4..77e0a15372 100644 --- a/voxygen/src/hud/social.rs +++ b/voxygen/src/hud/social.rs @@ -505,7 +505,7 @@ impl<'a> Widget for Social<'a> { }) .or_else(|| { self.selected_entity - .and_then(|s| self.client.state().read_component_copied(s.0)) + .and_then(|s| self.client.state().read_component_cloned(s.0)) }) .filter(|selected| { // Prevent inviting entities already in the same group @@ -564,7 +564,7 @@ impl<'a> Widget for Social<'a> { }); } } - } // End of Online Tab + } // End of Online Tab events } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index f825ac585b..aafde9bc41 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -581,7 +581,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::character::StandAnimation::update_skeleton( @@ -823,7 +823,7 @@ impl FigureMgr { ) }, CharacterState::Wielding { .. } => { - if physics.in_fluid { + if physics.in_fluid.is_some() { anim::character::SwimWieldAnimation::update_skeleton( &target_base, (active_tool_kind, second_tool_kind, vel.0.magnitude(), time), @@ -949,7 +949,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1047,7 +1047,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1143,7 +1143,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => { @@ -1237,7 +1237,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton( @@ -1329,7 +1329,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton( @@ -1404,7 +1404,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::dragon::IdleAnimation::update_skeleton( @@ -1478,7 +1478,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::critter::IdleAnimation::update_skeleton( @@ -1553,7 +1553,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::bird_small::IdleAnimation::update_skeleton( @@ -1628,7 +1628,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::fish_small::IdleAnimation::update_skeleton( @@ -1703,7 +1703,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::biped_large::IdleAnimation::update_skeleton( @@ -1795,7 +1795,7 @@ impl FigureMgr { let target_base = match ( physics.on_ground, vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving - physics.in_fluid, // In water + physics.in_fluid.is_some(), // In water ) { // Standing (true, false, false) => anim::golem::IdleAnimation::update_skeleton( diff --git a/world/src/layer/mod.rs b/world/src/layer/mod.rs index 6d989cb4f6..1a334b6d63 100644 --- a/world/src/layer/mod.rs +++ b/world/src/layer/mod.rs @@ -31,49 +31,44 @@ pub fn apply_scatter_to<'a>( chunk: &SimChunk, ) { use BlockKind::*; - let scatter: &[(_, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ + let scatter: &[(_, bool, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ // (density, Option<(wavelen, threshold)>) - (BlueFlower, |c| { + (BlueFlower, false, |c| { ( close(c.temp, -0.3, 0.7).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)), ) }), - (PinkFlower, |c| { + (PinkFlower, false, |c| { ( close(c.temp, 0.15, 0.5).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.6)), ) }), - (DeadBush, |c| { + (DeadBush, false, |c| { ( close(c.temp, 0.8, 0.3).min(close(c.humidity, 0.0, 0.4)) * 0.015, None, ) }), - (Twigs, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), - (Stones, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), - (ShortGrass, |c| { - ( - close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05, - Some((48.0, 0.4)), - ) - }), - (MediumGrass, |c| { + (Twigs, false, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), + (Stones, false, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), + (ShortGrass, false, |c| (close(c.temp, 0.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.4)))), + (MediumGrass, false, |c| { ( close(c.temp, 0.0, 0.6).min(close(c.humidity, 0.6, 0.35)) * 0.05, Some((48.0, 0.2)), ) }), - (LongGrass, |c| { + (LongGrass, false, |c| { ( close(c.temp, 0.4, 0.4).min(close(c.humidity, 0.8, 0.2)) * 0.05, Some((48.0, 0.1)), ) }), - (GrassSnow, |c| { + (GrassSnow, false, |c| { ( - close(c.temp, -0.4, 0.4).min(close(c.rockiness, 0.0, 0.5)), + close(c.temp, -0.4, 0.4).min(close(c.rockiness, 0.0, 0.5)) * 0.1, Some((48.0, 0.6)), ) }), @@ -92,7 +87,9 @@ pub fn apply_scatter_to<'a>( continue; }; - let bk = scatter.iter().enumerate().find_map(|(i, (bk, f))| { + let underwater = col_sample.water_level > col_sample.alt; + + let bk = scatter.iter().enumerate().find_map(|(i, (bk, is_underwater, f))| { let (density, patch) = f(chunk); if density <= 0.0 || patch @@ -105,6 +102,7 @@ pub fn apply_scatter_to<'a>( }) .unwrap_or(false) || !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) + || underwater != *is_underwater { None } else {