Better surface swimming, no underwater sprites

This commit is contained in:
Joshua Barretto 2020-08-11 12:13:18 +01:00
parent c1b07465cf
commit 6992194ad4
17 changed files with 119 additions and 101 deletions

View File

@ -396,7 +396,7 @@ impl Client {
} }
pub fn pick_up(&mut self, entity: EcsEntity) { 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 self.singleton_stream
.send(ClientMsg::ControlEvent(ControlEvent::InventoryManip( .send(ClientMsg::ControlEvent(ControlEvent::InventoryManip(
InventoryManip::Pickup(uid), InventoryManip::Pickup(uid),
@ -520,7 +520,7 @@ impl Client {
} }
pub fn mount(&mut self, entity: EcsEntity) { 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 self.singleton_stream
.send(ClientMsg::ControlEvent(ControlEvent::Mount(uid))) .send(ClientMsg::ControlEvent(ControlEvent::Mount(uid)))
.unwrap(); .unwrap();
@ -1294,7 +1294,7 @@ impl Client {
pub fn entity(&self) -> EcsEntity { self.entity } pub fn entity(&self) -> EcsEntity { self.entity }
/// Get the player's Uid. /// Get the player's Uid.
pub fn uid(&self) -> Option<Uid> { self.state.read_component_copied(self.entity) } pub fn uid(&self) -> Option<Uid> { self.state.read_component_cloned(self.entity) }
/// Get the client state /// Get the client state
pub fn get_client_state(&self) -> ClientState { self.client_state } pub fn get_client_state(&self) -> ClientState { self.client_state }
@ -1347,7 +1347,7 @@ impl Client {
pub fn is_admin(&self) -> bool { pub fn is_admin(&self) -> bool {
let client_uid = self let client_uid = self
.state .state
.read_component_copied::<Uid>(self.entity) .read_component_cloned::<Uid>(self.entity)
.expect("Client doesn't have a Uid!!!"); .expect("Client doesn't have a Uid!!!");
self.player_list self.player_list

View File

@ -1,5 +1,4 @@
use crate::{path::Chaser, sync::Uid, comp::Body}; use crate::{path::Chaser, sync::Uid, comp::Body};
use serde::{Deserialize, Serialize};
use specs::{Component, Entity as EcsEntity}; use specs::{Component, Entity as EcsEntity};
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
use vek::*; use vek::*;

View File

@ -80,7 +80,7 @@ pub struct PhysicsState {
pub on_ceiling: bool, pub on_ceiling: bool,
pub on_wall: Option<Vec3<f32>>, pub on_wall: Option<Vec3<f32>>,
pub touch_entity: Option<Uid>, pub touch_entity: Option<Uid>,
pub in_fluid: bool, pub in_fluid: Option<f32>, // Depth
} }
impl PhysicsState { impl PhysicsState {

View File

@ -199,8 +199,8 @@ impl State {
} }
/// Read a component attributed to a particular entity. /// Read a component attributed to a particular entity.
pub fn read_component_copied<C: Component + Copy>(&self, entity: EcsEntity) -> Option<C> { pub fn read_component_cloned<C: Component + Copy>(&self, entity: EcsEntity) -> Option<C> {
self.ecs.read_storage().get(entity).copied() self.ecs.read_storage().get(entity).cloned()
} }
/// Get a read-only reference to the storage of a particular component type. /// Get a read-only reference to the storage of a particular component type.

View File

@ -24,7 +24,7 @@ impl CharacterBehavior for Data {
update.character = CharacterState::GlideWield; update.character = CharacterState::GlideWield;
return update; return update;
} }
if data.physics.in_fluid { if data.physics.in_fluid.map(|depth| depth > 0.5).unwrap_or(false) {
update.character = CharacterState::Idle; update.character = CharacterState::Idle;
} }
// If there is a wall in front of character and they are trying to climb go to // If there is a wall in front of character and they are trying to climb go to

View File

@ -19,7 +19,7 @@ impl CharacterBehavior for Data {
if !data.physics.on_ground { if !data.physics.on_ground {
update.character = CharacterState::Glide; 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; update.character = CharacterState::Idle;
} }

View File

@ -8,7 +8,7 @@ use crate::{
sys::{character_behavior::JoinData, phys::GRAVITY}, sys::{character_behavior::JoinData, phys::GRAVITY},
util::Dir, util::Dir,
}; };
use vek::vec::Vec2; use vek::*;
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
const BASE_HUMANOID_AIR_ACCEL: f32 = 8.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` /// Handles updating `Components` to move player based on state of `JoinData`
pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) { pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
if data.physics.in_fluid { if let Some(depth) = data.physics.in_fluid {
swim_move(data, update, efficiency); swim_move(data, update, efficiency, depth);
} else { } else {
basic_move(data, update, efficiency); 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 /// 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 velocity
update.vel.0 += Vec2::broadcast(data.dt.0) update.vel.0 += Vec2::broadcast(data.dt.0)
* data.inputs.move_dir * data.inputs.move_dir
@ -120,7 +120,7 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
// Swim // Swim
if data.inputs.swimup.is_pressed() { if data.inputs.swimup.is_pressed() {
update.vel.0.z = 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 // Swim
if data.inputs.swimdown.is_pressed() { 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 /// Checks that player can wield the glider and updates `CharacterState` if so
pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) { 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; update.character = CharacterState::GlideWield;
} }
} }
/// Checks that player can jump and sends jump event if so /// Checks that player can jump and sends jump event if so
pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) { 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 update
.local_events .local_events
.push_front(LocalEvent::Jump(data.entity)); .push_front(LocalEvent::Jump(data.entity));

View File

@ -241,7 +241,8 @@ impl<'a> System<'a> for Sys {
bearing.xy().try_normalized().unwrap_or(Vec2::zero()) bearing.xy().try_normalized().unwrap_or(Vec2::zero())
* speed.min(0.2 + (dist - AVG_FOLLOW_DIST) / 8.0); * speed.min(0.2 + (dist - AVG_FOLLOW_DIST) / 8.0);
inputs.jump.set_state(bearing.z > 1.5); 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 { } else {
do_idle = true; do_idle = true;
@ -331,7 +332,8 @@ impl<'a> System<'a> for Sys {
.unwrap_or(Vec2::zero()) .unwrap_or(Vec2::zero())
* speed; * speed;
inputs.jump.set_state(bearing.z > 1.5); 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 { } else {
do_idle = true; do_idle = true;
@ -399,7 +401,8 @@ impl<'a> System<'a> for Sys {
.unwrap_or(Vec2::zero()) .unwrap_or(Vec2::zero())
* speed; * speed;
inputs.jump.set_state(bearing.z > 1.5); 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) if dist_sqrd < 16.0f32.powf(2.0)

View File

@ -13,6 +13,7 @@ use specs::{
saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage, saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage,
}; };
use vek::*; use vek::*;
use std::ops::Range;
pub const GRAVITY: f32 = 9.81 * 5.0; pub const GRAVITY: f32 = 9.81 * 5.0;
const BOUYANCY: f32 = 1.0; const BOUYANCY: f32 = 1.0;
@ -123,7 +124,7 @@ impl<'a> System<'a> for Sys {
} else { } else {
0.0 0.0
}) })
.max(if physics_state.in_fluid { .max(if physics_state.in_fluid.is_some() {
FRIC_FLUID FRIC_FLUID
} else { } else {
0.0 0.0
@ -133,7 +134,7 @@ impl<'a> System<'a> for Sys {
.is_some(); .is_some();
let downward_force = if !in_loaded_chunk { let downward_force = if !in_loaded_chunk {
0.0 // No gravity in unloaded chunks 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 (1.0 - BOUYANCY) * GRAVITY
} else { } else {
GRAVITY GRAVITY
@ -175,18 +176,24 @@ impl<'a> System<'a> for Sys {
.flatten() .flatten()
.flatten(); .flatten();
// Function for determining whether the player at a specific position collides // Function for iterating over the blocks the player at a specific position collides
// with the ground // with
let collision_with = |pos: Vec3<f32>, fn collision_iter<'a>(
hit: &dyn Fn(&Block) -> bool, pos: Vec3<f32>,
near_iter| { terrain: &'a TerrainGrid,
for (i, j, k) in near_iter { hit: &'a dyn Fn(&Block) -> bool,
near_iter: impl Iterator<Item=(i32, i32, i32)> + 'a,
radius: f32,
z_range: Range<f32>,
) -> impl Iterator<Item=Aabb<f32>> + 'a {
near_iter
.filter_map(move |(i, j, k)| {
let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(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) { if let Some(block) = terrain.get(block_pos).ok().copied().filter(hit) {
let player_aabb = Aabb { let player_aabb = Aabb {
min: pos + Vec3::new(-radius, -radius, z_min), min: pos + Vec3::new(-radius, -radius, z_range.start),
max: pos + Vec3::new(radius, radius, z_max), max: pos + Vec3::new(radius, radius, z_range.end),
}; };
let block_aabb = Aabb { let block_aabb = Aabb {
min: block_pos.map(|e| e as f32), min: block_pos.map(|e| e as f32),
@ -195,11 +202,21 @@ impl<'a> System<'a> for Sys {
}; };
if player_aabb.collides_with_aabb(block_aabb) { if player_aabb.collides_with_aabb(block_aabb) {
return true; 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<f32>,
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; 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 // Figure out if we're in water
physics_state.in_fluid = physics_state.in_fluid = collision_iter(pos.0, &terrain, &|block| block.is_fluid(), near_iter.clone(), radius, z_min..z_max)
collision_with(pos.0, &|block| block.is_fluid(), near_iter.clone()); .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 => { Collider::Point => {
let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta).ignore_error().cast(); let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta).ignore_error().cast();

View File

@ -262,7 +262,7 @@ fn handle_jump(
action: &ChatCommand, action: &ChatCommand,
) { ) {
if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) { if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) {
match server.state.read_component_copied::<comp::Pos>(target) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(current_pos) => { Some(current_pos) => {
server server
.state .state
@ -287,7 +287,7 @@ fn handle_goto(
if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) { if let Ok((x, y, z)) = scan_fmt!(&args, &action.arg_fmt(), f32, f32, f32) {
if server if server
.state .state
.read_component_copied::<comp::Pos>(target) .read_component_cloned::<comp::Pos>(target)
.is_some() .is_some()
{ {
server server
@ -498,9 +498,9 @@ fn handle_tp(
); );
return; return;
}; };
if let Some(_pos) = server.state.read_component_copied::<comp::Pos>(target) { if let Some(_pos) = server.state.read_component_cloned::<comp::Pos>(target) {
if let Some(player) = opt_player { if let Some(player) = opt_player {
if let Some(pos) = server.state.read_component_copied::<comp::Pos>(player) { if let Some(pos) = server.state.read_component_cloned::<comp::Pos>(player) {
server.state.write_component(target, pos); server.state.write_component(target, pos);
server.state.write_component(target, comp::ForceUpdate); server.state.write_component(target, comp::ForceUpdate);
} else { } else {
@ -545,7 +545,7 @@ fn handle_spawn(
(Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount, opt_ai) => { (Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount, opt_ai) => {
let uid = server let uid = server
.state .state
.read_component_copied(target) .read_component_cloned(target)
.expect("Expected player to have a UID"); .expect("Expected player to have a UID");
if let Some(alignment) = parse_alignment(uid, &opt_align) { if let Some(alignment) = parse_alignment(uid, &opt_align) {
let amount = opt_amount let amount = opt_amount
@ -556,7 +556,7 @@ fn handle_spawn(
let ai = opt_ai.unwrap_or_else(|| "true".to_string()); let ai = opt_ai.unwrap_or_else(|| "true".to_string());
match server.state.read_component_copied::<comp::Pos>(target) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
let agent = let agent =
if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment { if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment {
@ -666,7 +666,7 @@ fn handle_spawn_training_dummy(
_args: String, _args: String,
_action: &ChatCommand, _action: &ChatCommand,
) { ) {
match server.state.read_component_copied::<comp::Pos>(target) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
let vel = Vec3::new( let vel = Vec3::new(
rand::thread_rng().gen_range(-2.0, 3.0), rand::thread_rng().gen_range(-2.0, 3.0),
@ -707,7 +707,7 @@ fn handle_spawn_campfire(
_args: String, _args: String,
_action: &ChatCommand, _action: &ChatCommand,
) { ) {
match server.state.read_component_copied::<comp::Pos>(target) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
server server
.state .state
@ -1066,7 +1066,7 @@ fn handle_explosion(
let ecs = server.state.ecs(); let ecs = server.state.ecs();
match server.state.read_component_copied::<comp::Pos>(target) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
ecs.read_resource::<EventBus<ServerEvent>>() ecs.read_resource::<EventBus<ServerEvent>>()
.emit_now(ServerEvent::Explosion { .emit_now(ServerEvent::Explosion {
@ -1090,7 +1090,7 @@ fn handle_waypoint(
_args: String, _args: String,
_action: &ChatCommand, _action: &ChatCommand,
) { ) {
match server.state.read_component_copied::<comp::Pos>(target) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
let time = server.state.ecs().read_resource(); let time = server.state.ecs().read_resource();
let _ = server let _ = server
@ -1126,7 +1126,7 @@ fn handle_adminify(
Some(player) => { Some(player) => {
let is_admin = if server let is_admin = if server
.state .state
.read_component_copied::<comp::Admin>(player) .read_component_cloned::<comp::Admin>(player)
.is_some() .is_some()
{ {
ecs.write_storage::<comp::Admin>().remove(player); ecs.write_storage::<comp::Admin>().remove(player);
@ -1670,7 +1670,7 @@ fn handle_remove_lights(
action: &ChatCommand, action: &ChatCommand,
) { ) {
let opt_radius = scan_fmt_some!(&args, &action.arg_fmt(), f32); let opt_radius = scan_fmt_some!(&args, &action.arg_fmt(), f32);
let opt_player_pos = server.state.read_component_copied::<comp::Pos>(target); let opt_player_pos = server.state.read_component_cloned::<comp::Pos>(target);
let mut to_delete = vec![]; let mut to_delete = vec![];
match opt_player_pos { match opt_player_pos {

View File

@ -251,7 +251,7 @@ pub fn handle_respawn(server: &Server, entity: EcsEntity) {
.is_some() .is_some()
{ {
let respawn_point = state let respawn_point = state
.read_component_copied::<comp::Waypoint>(entity) .read_component_cloned::<comp::Waypoint>(entity)
.map(|wp| wp.get_pos()) .map(|wp| wp.get_pos())
.unwrap_or(state.ecs().read_resource::<SpawnPoint>().0); .unwrap_or(state.ecs().read_resource::<SpawnPoint>().0);

View File

@ -167,10 +167,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
thrown_items.push(( thrown_items.push((
*pos, *pos,
state state
.read_component_copied::<comp::Vel>(entity) .read_component_cloned::<comp::Vel>(entity)
.unwrap_or_default(), .unwrap_or_default(),
state state
.read_component_copied::<comp::Ori>(entity) .read_component_cloned::<comp::Ori>(entity)
.unwrap_or_default(), .unwrap_or_default(),
*kind, *kind,
)); ));
@ -185,7 +185,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
state.read_storage::<comp::Pos>().get(entity) state.read_storage::<comp::Pos>().get(entity)
{ {
let uid = state let uid = state
.read_component_copied(entity) .read_component_cloned(entity)
.expect("Expected player to have a UID"); .expect("Expected player to have a UID");
if ( if (
&state.read_storage::<comp::Alignment>(), &state.read_storage::<comp::Alignment>(),
@ -341,7 +341,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
dropped_items.push(( dropped_items.push((
*pos, *pos,
state state
.read_component_copied::<comp::Ori>(entity) .read_component_cloned::<comp::Ori>(entity)
.unwrap_or_default(), .unwrap_or_default(),
item, item,
)); ));
@ -373,10 +373,10 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
for _ in 0..amount { for _ in 0..amount {
dropped_items.push(( dropped_items.push((
state state
.read_component_copied::<comp::Pos>(entity) .read_component_cloned::<comp::Pos>(entity)
.unwrap_or_default(), .unwrap_or_default(),
state state
.read_component_copied::<comp::Ori>(entity) .read_component_cloned::<comp::Ori>(entity)
.unwrap_or_default(), .unwrap_or_default(),
item.clone(), item.clone(),
)); ));
@ -407,7 +407,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
+ Vec3::unit_z() * 15.0 + Vec3::unit_z() * 15.0
+ Vec3::<f32>::zero().map(|_| rand::thread_rng().gen::<f32>() - 0.5) * 4.0; + Vec3::<f32>::zero().map(|_| rand::thread_rng().gen::<f32>() - 0.5) * 4.0;
let uid = state.read_component_copied::<Uid>(entity); let uid = state.read_component_cloned::<Uid>(entity);
let mut new_entity = state let mut new_entity = state
.create_object(Default::default(), match kind { .create_object(Default::default(), match kind {

View File

@ -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 // Note: If other `ServerEvent`s are referring to this entity they will be
// disrupted // disrupted
let maybe_client = state.ecs().write_storage::<Client>().remove(entity); let maybe_client = state.ecs().write_storage::<Client>().remove(entity);
let maybe_uid = state.read_component_copied::<Uid>(entity); let maybe_uid = state.read_component_cloned::<Uid>(entity);
let maybe_player = state.ecs().write_storage::<comp::Player>().remove(entity); let maybe_player = state.ecs().write_storage::<comp::Player>().remove(entity);
let maybe_group = state let maybe_group = state
.ecs() .ecs()

View File

@ -173,7 +173,7 @@ impl StateExt for State {
self.write_component(entity, comp::CharacterState::default()); self.write_component(entity, comp::CharacterState::default());
self.write_component( self.write_component(
entity, 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 // Set the character id for the player
@ -213,7 +213,7 @@ impl StateExt for State {
// Notify clients of a player list update // Notify clients of a player list update
let client_uid = self let client_uid = self
.read_component_copied::<Uid>(entity) .read_component_cloned::<Uid>(entity)
.map(|u| u) .map(|u| u)
.expect("Client doesn't have a Uid!!!"); .expect("Client doesn't have a Uid!!!");

View File

@ -505,7 +505,7 @@ impl<'a> Widget for Social<'a> {
}) })
.or_else(|| { .or_else(|| {
self.selected_entity 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| { .filter(|selected| {
// Prevent inviting entities already in the same group // Prevent inviting entities already in the same group

View File

@ -581,7 +581,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::character::StandAnimation::update_skeleton( (true, false, false) => anim::character::StandAnimation::update_skeleton(
@ -823,7 +823,7 @@ impl FigureMgr {
) )
}, },
CharacterState::Wielding { .. } => { CharacterState::Wielding { .. } => {
if physics.in_fluid { if physics.in_fluid.is_some() {
anim::character::SwimWieldAnimation::update_skeleton( anim::character::SwimWieldAnimation::update_skeleton(
&target_base, &target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time), (active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
@ -949,7 +949,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => { (true, false, false) => {
@ -1047,7 +1047,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => { (true, false, false) => {
@ -1143,7 +1143,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => { (true, false, false) => {
@ -1237,7 +1237,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton( (true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton(
@ -1329,7 +1329,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton( (true, false, false) => anim::fish_medium::IdleAnimation::update_skeleton(
@ -1404,7 +1404,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::dragon::IdleAnimation::update_skeleton( (true, false, false) => anim::dragon::IdleAnimation::update_skeleton(
@ -1478,7 +1478,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::critter::IdleAnimation::update_skeleton( (true, false, false) => anim::critter::IdleAnimation::update_skeleton(
@ -1553,7 +1553,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::bird_small::IdleAnimation::update_skeleton( (true, false, false) => anim::bird_small::IdleAnimation::update_skeleton(
@ -1628,7 +1628,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::fish_small::IdleAnimation::update_skeleton( (true, false, false) => anim::fish_small::IdleAnimation::update_skeleton(
@ -1703,7 +1703,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::biped_large::IdleAnimation::update_skeleton( (true, false, false) => anim::biped_large::IdleAnimation::update_skeleton(
@ -1795,7 +1795,7 @@ impl FigureMgr {
let target_base = match ( let target_base = match (
physics.on_ground, physics.on_ground,
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
physics.in_fluid, // In water physics.in_fluid.is_some(), // In water
) { ) {
// Standing // Standing
(true, false, false) => anim::golem::IdleAnimation::update_skeleton( (true, false, false) => anim::golem::IdleAnimation::update_skeleton(

View File

@ -31,49 +31,44 @@ pub fn apply_scatter_to<'a>(
chunk: &SimChunk, chunk: &SimChunk,
) { ) {
use BlockKind::*; use BlockKind::*;
let scatter: &[(_, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[ let scatter: &[(_, bool, fn(&SimChunk) -> (f32, Option<(f32, f32)>))] = &[
// (density, Option<(wavelen, threshold)>) // (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, close(c.temp, -0.3, 0.7).min(close(c.humidity, 0.6, 0.35)) * 0.05,
Some((48.0, 0.6)), 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, close(c.temp, 0.15, 0.5).min(close(c.humidity, 0.6, 0.35)) * 0.05,
Some((48.0, 0.6)), 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, close(c.temp, 0.8, 0.3).min(close(c.humidity, 0.0, 0.4)) * 0.015,
None, None,
) )
}), }),
(Twigs, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)), (Twigs, false, |c| ((c.tree_density - 0.5).max(0.0) * 0.0025, None)),
(Stones, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)), (Stones, false, |c| ((c.rockiness - 0.5).max(0.0) * 0.005, None)),
(ShortGrass, |c| { (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.3, 0.4).min(close(c.humidity, 0.6, 0.35)) * 0.05,
Some((48.0, 0.4)),
)
}),
(MediumGrass, |c| {
( (
close(c.temp, 0.0, 0.6).min(close(c.humidity, 0.6, 0.35)) * 0.05, close(c.temp, 0.0, 0.6).min(close(c.humidity, 0.6, 0.35)) * 0.05,
Some((48.0, 0.2)), 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, close(c.temp, 0.4, 0.4).min(close(c.humidity, 0.8, 0.2)) * 0.05,
Some((48.0, 0.1)), 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)), Some((48.0, 0.6)),
) )
}), }),
@ -92,7 +87,9 @@ pub fn apply_scatter_to<'a>(
continue; 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); let (density, patch) = f(chunk);
if density <= 0.0 if density <= 0.0
|| patch || patch
@ -105,6 +102,7 @@ pub fn apply_scatter_to<'a>(
}) })
.unwrap_or(false) .unwrap_or(false)
|| !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) || !RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
|| underwater != *is_underwater
{ {
None None
} else { } else {