From 338b377ef435dc3328fd36f7c2c65f6601897bef Mon Sep 17 00:00:00 2001 From: Joshua Yanovski Date: Wed, 28 Sep 2022 13:46:39 -0700 Subject: [PATCH] Remove write to Body from Buff. Also fix tests and add an inner PhysicsState type that is Copy, to make it easier to facilitate reading Last and read deltas. --- common/src/comp/ability.rs | 2 +- common/src/comp/mod.rs | 4 +- common/src/comp/phys.rs | 26 ++++++--- common/src/event.rs | 5 ++ common/src/states/basic_beam.rs | 4 +- common/src/states/climb.rs | 6 +-- common/src/states/dance.rs | 2 +- common/src/states/glide.rs | 9 ++-- common/src/states/glide_wield.rs | 4 +- common/src/states/idle.rs | 2 +- common/src/states/leap_melee.rs | 2 +- common/src/states/sit.rs | 2 +- common/src/states/skate.rs | 2 +- common/src/states/spin_melee.rs | 2 +- common/src/states/utils.rs | 37 +++++++------ common/src/states/wallrun.rs | 6 +-- common/src/states/wielding.rs | 4 +- common/state/src/state.rs | 3 +- common/systems/src/buff.rs | 25 +++++---- common/systems/src/phys.rs | 53 ++++++++++--------- common/systems/src/projectile.rs | 4 +- common/systems/src/shockwave.rs | 2 +- common/systems/src/stats.rs | 5 +- common/systems/tests/character_state.rs | 20 +++++-- common/systems/tests/phys/utils.rs | 10 +++- server/agent/src/action_nodes.rs | 4 +- server/src/events/entity_manipulation.rs | 10 +++- server/src/events/mod.rs | 5 +- server/src/sys/agent.rs | 6 +-- server/src/sys/agent/behavior_tree.rs | 2 +- server/src/sys/object.rs | 2 +- server/src/sys/pets.rs | 2 +- server/src/sys/waypoint.rs | 2 +- voxygen/egui/src/lib.rs | 10 ++-- .../audio/sfx/event_mapper/movement/mod.rs | 9 ++-- .../audio/sfx/event_mapper/movement/tests.rs | 14 ++--- voxygen/src/scene/figure/mod.rs | 1 + voxygen/src/scene/mod.rs | 2 +- voxygen/src/session/mod.rs | 3 +- 39 files changed, 185 insertions(+), 128 deletions(-) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index b552a8ed2d..4b46afeaef 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -655,7 +655,7 @@ impl CharacterAbility { pub fn requirements_paid(&self, data: &JoinData, update: &mut StateUpdate) -> bool { match self { CharacterAbility::Roll { energy_cost, .. } => { - data.physics.on_ground.is_some() + data.physics.state.on_ground.is_some() && data.inputs.move_dir.magnitude_squared() > 0.25 && update.energy.try_change_by(-*energy_cost).is_ok() }, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 54a7cf50e7..c8b0a57737 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -100,8 +100,8 @@ pub use self::{ ori::Ori, pet::Pet, phys::{ - Collider, Density, ForceUpdate, Immovable, Mass, PhysicsState, Pos, PosVelOriDefer, - PreviousPhysCache, Scale, Sticky, Vel, + Collider, Density, ForceUpdate, Immovable, Mass, PhysicsState, PhysicsStateFast, Pos, + PosVelOriDefer, PreviousPhysCache, Scale, Sticky, Vel, }, player::DisconnectReason, player::{AliasError, Player, MAX_ALIAS_LEN}, diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index 94308227c4..d899fe8123 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -170,13 +170,13 @@ impl Component for Immovable { type Storage = DerefFlaggedStorage>; } -// PhysicsState -#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] -pub struct PhysicsState { +/// Cheaply copyable components of PhysicsState used for most delta +/// computations. +#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct PhysicsStateFast { pub on_ground: Option, pub on_ceiling: bool, pub on_wall: Option>, - pub touch_entities: HashSet, pub in_fluid: Option, pub ground_vel: Vec3, pub footwear: Friction, @@ -184,6 +184,13 @@ pub struct PhysicsState { pub skating_active: bool, } +// PhysicsState +#[derive(Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct PhysicsState { + pub state: PhysicsStateFast, + pub touch_entities: HashSet, +} + impl PhysicsState { pub fn reset(&mut self) { // Avoid allocation overhead! @@ -191,12 +198,17 @@ impl PhysicsState { touch_entities.clear(); *self = Self { touch_entities, - ground_vel: self.ground_vel, /* Preserved, since it's the velocity of the last - * contact point */ - ..Self::default() + state: PhysicsStateFast { + ground_vel: self.state.ground_vel, /* Preserved, since it's the velocity of the + * last + * contact point */ + ..Default::default() + }, } } +} +impl PhysicsStateFast { pub fn on_surface(&self) -> Option> { self.on_ground .map(|_| -Vec3::unit_z()) diff --git a/common/src/event.rs b/common/src/event.rs index 23a3d808fa..8b3ce44e92 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -226,6 +226,11 @@ pub enum ServerEvent { entity: EcsEntity, update: comp::MapMarkerChange, }, + /// FIXME: Remove this hack! It is only used for dousing flames. + UpdateBody { + entity: EcsEntity, + new_body: comp::Body, + }, } pub struct EventBus { diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 65243eb6c9..27bee249a8 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -164,14 +164,14 @@ impl CharacterBehavior for Data { .prerotated(pitch) }; // Velocity relative to the current ground - let rel_vel = data.vel.0 - data.physics.ground_vel; + let rel_vel = data.vel.0 - data.physics.state.ground_vel; // Gets offsets let body_offsets = beam_offsets( data.body, data.inputs.look_dir, data.ori.look_vec(), rel_vel, - data.physics.on_ground, + data.physics.state.on_ground, ); let pos = Pos(data.pos.0 + body_offsets); diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 11ddb85530..97951d2989 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -60,9 +60,9 @@ impl CharacterBehavior for Data { // If no wall is in front of character or we stopped climbing; let (wall_dir, climb) = if let (Some(wall_dir), Some(climb), None) = ( - data.physics.on_wall, + data.physics.state.on_wall, data.inputs.climb, - data.physics.on_ground, + data.physics.state.on_ground, ) { (wall_dir, climb) } else { @@ -102,7 +102,7 @@ impl CharacterBehavior for Data { // Smooth orientation data.ori.slerped_towards( Ori::from(ori_dir), - if data.physics.on_ground.is_some() { + if data.physics.state.on_ground.is_some() { 9.0 } else { 2.0 diff --git a/common/src/states/dance.rs b/common/src/states/dance.rs index 171e04bb39..1370bcd938 100644 --- a/common/src/states/dance.rs +++ b/common/src/states/dance.rs @@ -19,7 +19,7 @@ impl CharacterBehavior for Data { handle_jump(data, output_events, &mut update, 1.0); // Try to Fall/Stand up/Move - if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { + if data.physics.state.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { update.character = CharacterState::Idle(idle::Data::default()); } diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 02a68c4c57..5729b98dbe 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -79,11 +79,11 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); // If player is on ground, end glide - if data.physics.on_ground.is_some() - && (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2) + if data.physics.state.on_ground.is_some() + && (data.vel.0 - data.physics.state.ground_vel).magnitude_squared() < 2_f32.powi(2) { update.character = CharacterState::GlideWield(glide_wield::Data::from(data)); - } else if data.physics.in_liquid().is_some() + } else if data.physics.state.in_liquid().is_some() || data .inventory .and_then(|inv| inv.equipped(EquipSlot::Glider)) @@ -93,6 +93,7 @@ impl CharacterBehavior for Data { } else if !handle_climb(data, &mut update) { let air_flow = data .physics + .state .in_fluid .map(|fluid| fluid.relative_flow(data.vel)) .unwrap_or_default(); @@ -171,7 +172,7 @@ impl CharacterBehavior for Data { Quaternion::rotation_3d( PI / 2.0 * accel_factor - * if data.physics.on_ground.is_some() { + * if data.physics.state.on_ground.is_some() { -1.0 } else { 1.0 diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index f0d0e0b361..3185ab0add 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -49,7 +49,7 @@ impl CharacterBehavior for Data { // If still in this state, do the things if matches!(update.character, CharacterState::GlideWield(_)) { // If not on the ground while wielding glider enter gliding state - update.character = if data.physics.on_ground.is_none() { + update.character = if data.physics.state.on_ground.is_none() { CharacterState::Glide(glide::Data::new( self.span_length, self.chord_length, @@ -60,7 +60,7 @@ impl CharacterBehavior for Data { .inventory .and_then(|inv| inv.equipped(EquipSlot::Glider)) .is_some() - && data.physics.in_liquid().map_or(true, |depth| depth < 0.5) + && data.physics.state.in_liquid().map_or(true, |depth| depth < 0.5) { CharacterState::GlideWield(Self { // Glider tilt follows look dir diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs index ae37b42d2a..ef71811622 100644 --- a/common/src/states/idle.rs +++ b/common/src/states/idle.rs @@ -30,7 +30,7 @@ impl CharacterBehavior for Data { // Try to Fall/Stand up/Move if self.is_sneaking - && (data.physics.on_ground.is_none() || data.physics.in_liquid().is_some()) + && (data.physics.state.on_ground.is_none() || data.physics.state.in_liquid().is_some()) { update.character = CharacterState::Idle(Data { is_sneaking: false, diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 56a6b330e4..89a913d183 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -90,7 +90,7 @@ impl CharacterBehavior for Data { timer: tick_attack_or_default(data, self.timer, None), ..*self }); - } else if data.physics.on_ground.is_some() | data.physics.in_liquid().is_some() { + } else if data.physics.state.on_ground.is_some() | data.physics.state.in_liquid().is_some() { // Transitions to swing portion of state upon hitting ground update.character = CharacterState::LeapMelee(Data { timer: Duration::default(), diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs index 3d19ae94f5..0af0509c52 100644 --- a/common/src/states/sit.rs +++ b/common/src/states/sit.rs @@ -19,7 +19,7 @@ impl CharacterBehavior for Data { handle_jump(data, output_events, &mut update, 1.0); // Try to Fall/Stand up/Move - if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { + if data.physics.state.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 { update.character = CharacterState::Idle(idle::Data::default()); } diff --git a/common/src/states/skate.rs b/common/src/states/skate.rs index 717ad0857f..25198b3759 100644 --- a/common/src/states/skate.rs +++ b/common/src/states/skate.rs @@ -39,7 +39,7 @@ impl CharacterBehavior for Data { handle_wield(data, &mut update); handle_jump(data, output_events, &mut update, 1.0); - if !data.physics.skating_active { + if !data.physics.state.skating_active { update.character = CharacterState::Idle(idle::Data { is_sneaking: false, footwear: Some(self.footwear), diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 36a3997ea1..0c500c87a2 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -62,7 +62,7 @@ impl CharacterBehavior for Data { match self.static_data.movement_behavior { MovementBehavior::ForwardGround | MovementBehavior::Stationary => {}, MovementBehavior::AxeHover => { - update.movement = update.movement.with_movement(if data.physics.on_ground.is_some() { + update.movement = update.movement.with_movement(if data.physics.state.on_ground.is_some() { // TODO: Just remove axehover entirely with axe rework, it's really janky // TODO: Should 5 even be used here, or should body accel be used? Maybe just call handle_move? let dir = Dir::from_unnormalized(data.inputs.move_dir.with_z(0.0)); diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 38320a491c..5c4a02bbe4 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -327,7 +327,7 @@ pub fn handle_skating(data: &JoinData, update: &mut StateUpdate) { footwear, }); } - if data.physics.skating_active { + if data.physics.state.skating_active { update.character = CharacterState::Skate(skate::Data::new(data, footwear.unwrap_or(Friction::Normal))); } @@ -338,16 +338,17 @@ pub fn handle_skating(data: &JoinData, update: &mut StateUpdate) { pub fn handle_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) { let submersion = data .physics + .state .in_liquid() .map(|depth| depth / data.body.height()); if input_is_pressed(data, InputKind::Fly) && submersion.map_or(true, |sub| sub < 1.0) - && (data.physics.on_ground.is_none() || data.body.jump_impulse().is_none()) + && (data.physics.state.on_ground.is_none() || data.body.jump_impulse().is_none()) && data.body.fly_thrust().is_some() { fly_move(data, update, efficiency); - } else if let Some(submersion) = (data.physics.on_ground.is_none() + } else if let Some(submersion) = (data.physics.state.on_ground.is_none() && data.body.swim_thrust().is_some()) .then_some(submersion) .flatten() @@ -362,7 +363,7 @@ pub fn handle_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f3 fn basic_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) { let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier; - let accel = if let Some(block) = data.physics.on_ground { + let accel = if let Some(block) = data.physics.state.on_ground { // FRIC_GROUND temporarily used to normalize things around expected values data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND } else { @@ -409,7 +410,7 @@ pub fn handle_forced_movement( match movement { ForcedMovement::Forward { strength } => { let strength = strength * data.stats.move_speed_modifier * data.stats.friction_modifier; - if let Some(accel) = data.physics.on_ground.map(|block| { + if let Some(accel) = data.physics.state.on_ground.map(|block| { // FRIC_GROUND temporarily used to normalize things around expected values data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND }) { @@ -474,7 +475,7 @@ pub fn handle_orientation( // unit is multiples of 180° let half_turns_per_tick = data.body.base_ori_rate() * efficiency - * if data.physics.on_ground.is_some() { + * if data.physics.state.on_ground.is_some() { 1.0 } else { 0.2 @@ -579,7 +580,7 @@ pub fn fly_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) Density((update.density.0 + data.dt.0 * rate * change).clamp(min, max)) }; let def_density = ship.density().0; - if data.physics.in_liquid().is_some() { + if data.physics.state.in_liquid().is_some() { let hull_density = ship.hull_density().0; update.density.0 = regulate_density(def_density * 0.6, hull_density, hull_density, 25.0).0; @@ -660,25 +661,25 @@ pub fn attempt_wield(data: &JoinData<'_>, update: &mut StateUpdate) { /// Checks that player can `Sit` and updates `CharacterState` if so pub fn attempt_sit(data: &JoinData<'_>, update: &mut StateUpdate) { - if data.physics.on_ground.is_some() { + if data.physics.state.on_ground.is_some() { update.character = CharacterState::Sit; } } pub fn attempt_dance(data: &JoinData<'_>, update: &mut StateUpdate) { - if data.physics.on_ground.is_some() && data.body.is_humanoid() { + if data.physics.state.on_ground.is_some() && data.body.is_humanoid() { update.character = CharacterState::Dance; } } pub fn attempt_talk(data: &JoinData<'_>, update: &mut StateUpdate) { - if data.physics.on_ground.is_some() { + if data.physics.state.on_ground.is_some() { update.character = CharacterState::Talk; } } pub fn attempt_sneak(data: &JoinData<'_>, update: &mut StateUpdate) { - if data.physics.on_ground.is_some() && data.body.is_humanoid() { + if data.physics.state.on_ground.is_some() && data.body.is_humanoid() { update.character = Idle(idle::Data { is_sneaking: true, footwear: data.character.footwear(), @@ -689,10 +690,11 @@ pub fn attempt_sneak(data: &JoinData<'_>, update: &mut StateUpdate) { /// Checks that player can `Climb` and updates `CharacterState` if so pub fn handle_climb(data: &JoinData<'_>, update: &mut StateUpdate) -> bool { if data.inputs.climb.is_some() - && data.physics.on_wall.is_some() - && data.physics.on_ground.is_none() + && data.physics.state.on_wall.is_some() + && data.physics.state.on_ground.is_none() && !data .physics + .state .in_liquid() .map(|depth| depth > 1.0) .unwrap_or(false) @@ -708,9 +710,9 @@ pub fn handle_climb(data: &JoinData<'_>, update: &mut StateUpdate) -> bool { } pub fn handle_wallrun(data: &JoinData<'_>, update: &mut StateUpdate) -> bool { - if data.physics.on_wall.is_some() - && data.physics.on_ground.is_none() - && data.physics.in_liquid().is_none() + if data.physics.state.on_wall.is_some() + && data.physics.state.on_ground.is_none() + && data.physics.state.in_liquid().is_none() && data.body.can_climb() { update.character = CharacterState::Wallrun(wallrun::Data); @@ -895,6 +897,7 @@ pub fn attempt_glide_wield( .is_some() && !data .physics + .state .in_liquid() .map(|depth| depth > 1.0) .unwrap_or(false) @@ -916,7 +919,7 @@ pub fn handle_jump( _update: &mut StateUpdate, strength: f32, ) -> bool { - (input_is_pressed(data, InputKind::Jump) && data.physics.on_ground.is_some()) + (input_is_pressed(data, InputKind::Jump) && data.physics.state.on_ground.is_some()) .then(|| data.body.jump_impulse()) .flatten() .map(|impulse| { diff --git a/common/src/states/wallrun.rs b/common/src/states/wallrun.rs index 0d0d8279f3..76c904d2d6 100644 --- a/common/src/states/wallrun.rs +++ b/common/src/states/wallrun.rs @@ -33,9 +33,9 @@ impl CharacterBehavior for Data { // fall off wall, hit ground, or enter water // TODO: Rugged way to determine when state change occurs and we need to leave // this state - if data.physics.on_wall.is_none() - || data.physics.on_ground.is_some() - || data.physics.in_liquid().is_some() + if data.physics.state.on_wall.is_none() + || data.physics.state.on_ground.is_some() + || data.physics.state.in_liquid().is_some() { update.character = CharacterState::Idle(idle::Data::default()); } diff --git a/common/src/states/wielding.rs b/common/src/states/wielding.rs index ebe089e58b..8e20b4623a 100644 --- a/common/src/states/wielding.rs +++ b/common/src/states/wielding.rs @@ -28,7 +28,7 @@ impl CharacterBehavior for Data { handle_jump(data, output_events, &mut update, 1.0); if self.is_sneaking - && (data.physics.on_ground.is_none() || data.physics.in_liquid().is_some()) + && (data.physics.state.on_ground.is_none() || data.physics.state.in_liquid().is_some()) { update.character = CharacterState::Wielding(Data { is_sneaking: false }); } @@ -96,7 +96,7 @@ impl CharacterBehavior for Data { fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { let mut update = StateUpdate::from(data); - if data.physics.on_ground.is_some() && data.body.is_humanoid() { + if data.physics.state.on_ground.is_some() && data.body.is_humanoid() { update.character = CharacterState::Wielding(Data { is_sneaking: true }); } update diff --git a/common/state/src/state.rs b/common/state/src/state.rs index ab501b9b44..d62fb247b5 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -575,7 +575,8 @@ impl State { match event { LocalEvent::Jump(entity, impulse) => { if let Some(vel) = velocities.get_mut(entity) { - vel.0.z = impulse + physics.get(entity).map_or(0.0, |ps| ps.ground_vel.z); + vel.0.z = + impulse + physics.get(entity).map_or(0.0, |ps| ps.state.ground_vel.z); } }, LocalEvent::ApplyImpulse { entity, impulse } => { diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index cc246e0869..5b7e2bae81 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -31,6 +31,7 @@ pub struct ReadData<'a> { entities: Entities<'a>, dt: Read<'a, DeltaTime>, server_bus: Read<'a, EventBus>, + bodies: ReadStorage<'a, Body>, inventories: ReadStorage<'a, Inventory>, healths: ReadStorage<'a, Health>, energies: ReadStorage<'a, Energy>, @@ -48,7 +49,6 @@ impl<'a> System<'a> for Sys { ReadData<'a>, WriteStorage<'a, Buffs>, WriteStorage<'a, Stats>, - WriteStorage<'a, Body>, WriteStorage<'a, LightEmitter>, ); @@ -58,7 +58,7 @@ impl<'a> System<'a> for Sys { fn run( job: &mut Job, - (read_data, mut buffs, mut stats, mut bodies, mut light_emitters): Self::SystemData, + (read_data, mut buffs, mut stats, mut light_emitters): Self::SystemData, ) { let mut server_emitter = read_data.server_bus.emitter(); let dt = read_data.dt.0; @@ -74,7 +74,7 @@ impl<'a> System<'a> for Sys { prof_span!(guard_, "buff campfire deactivate"); ( &read_data.entities, - &mut bodies, + &read_data.bodies, &read_data.physics_states, light_emitters_mask, //to improve iteration speed ) @@ -82,16 +82,19 @@ impl<'a> System<'a> for Sys { .filter(|(_, body, physics_state, _)| { matches!(&**body, Body::Object(object::Body::CampfireLit)) && matches!( - physics_state.in_fluid, + physics_state.state.in_fluid, Some(Fluid::Liquid { kind: LiquidKind::Water, .. }) ) }) - .for_each(|(e, mut body, _, _)| { - *body = Body::Object(object::Body::Campfire); - light_emitters.remove(e); + .for_each(|(entity, _, _, _)| { + server_emitter.emit(ServerEvent::UpdateBody { + entity, + new_body: Body::Object(object::Body::Campfire), + }); + light_emitters.remove(entity); }); drop(guard_); @@ -108,7 +111,7 @@ impl<'a> System<'a> for Sys { // Apply buffs to entity based off of their current physics_state if let Some(physics_state) = physics_state { if matches!( - physics_state.on_ground.and_then(|b| b.get_sprite()), + physics_state.state.on_ground.and_then(|b| b.get_sprite()), Some(SpriteKind::EnsnaringVines) | Some(SpriteKind::EnsnaringWeb) ) { // If on ensnaring vines, apply ensnared debuff @@ -123,7 +126,7 @@ impl<'a> System<'a> for Sys { }); } if matches!( - physics_state.on_ground.and_then(|b| b.get_sprite()), + physics_state.state.on_ground.and_then(|b| b.get_sprite()), Some(SpriteKind::SeaUrchin) ) { // If touching Sea Urchin apply Bleeding buff @@ -138,7 +141,7 @@ impl<'a> System<'a> for Sys { }); } if matches!( - physics_state.in_fluid, + physics_state.state.in_fluid, Some(Fluid::Liquid { kind: LiquidKind::Lava, .. @@ -155,7 +158,7 @@ impl<'a> System<'a> for Sys { )), }); } else if matches!( - physics_state.in_fluid, + physics_state.state.in_fluid, Some(Fluid::Liquid { kind: LiquidKind::Water, .. diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index fa313808de..e3cc9b7099 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -4,7 +4,8 @@ use common::{ fluid_dynamics::{Fluid, LiquidKind, Wings}, inventory::item::armor::Friction, Body, CharacterState, Collider, Density, Immovable, Mass, MovementState, Ori, PhysicsState, - Pos, PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel, + PhysicsStateFast, Pos, PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, + Vel, }, consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY}, event::{EventBus, ServerEvent}, @@ -388,7 +389,7 @@ impl<'a> PhysicsRead<'a> { )| { let is_sticky = sticky.is_some(); let is_immovable = immovable.is_some(); - let is_mid_air = physics.on_surface().is_none(); + let is_mid_air = physics.state.on_surface().is_none(); let mut entity_entity_collision_checks = 0; let mut entity_entity_collisions = 0; @@ -667,7 +668,7 @@ impl<'a> PhysicsData<'a> { // Apply physics only if in a loaded chunk if in_loaded_chunk // And not already stuck on a block (e.g., for arrows) - && !(physics_state.on_surface().is_some() && sticky.is_some()) + && !(physics_state.state.on_surface().is_some() && sticky.is_some()) { // Clamp dt to an effective 10 TPS, to prevent gravity // from slamming the players into the floor when @@ -675,7 +676,7 @@ impl<'a> PhysicsData<'a> { // to lag (as observed in the 0.9 release party). let dt = DeltaTime(read.dt.0.min(0.1)); - match physics_state.in_fluid { + match physics_state.state.in_fluid { None => { vel.0.z -= dt.0 * GRAVITY; }, @@ -814,8 +815,8 @@ impl<'a> PhysicsData<'a> { if let Some(state) = character_state { let footwear = state.footwear().unwrap_or(Friction::Normal); - if footwear != physics_state.footwear { - physics_state.footwear = footwear; + if footwear != physics_state.state.footwear { + physics_state.state.footwear = footwear; } } @@ -849,7 +850,7 @@ impl<'a> PhysicsData<'a> { // velocities or entirely broken position snapping. let mut tgt_pos = pos.0 + pos_delta; - let was_on_ground = physics_state.on_ground.is_some(); + let was_on_ground = physics_state.state.on_ground.is_some(); let block_snap = body.map_or(false, |b| !matches!(b, Body::Object(_) | Body::Ship(_))); let climbing = @@ -876,7 +877,7 @@ impl<'a> PhysicsData<'a> { &mut cpos, tgt_pos, &mut vel, - physics_state, + &mut physics_state.state, Vec3::zero(), &read.dt, was_on_ground, @@ -909,7 +910,7 @@ impl<'a> PhysicsData<'a> { &mut cpos, tgt_pos, &mut vel, - physics_state, + &mut physics_state.state, Vec3::zero(), &read.dt, was_on_ground, @@ -921,8 +922,8 @@ impl<'a> PhysicsData<'a> { ); // Sticky things shouldn't move when on a surface - if physics_state.on_surface().is_some() && sticky.is_some() { - vel.0 = physics_state.ground_vel; + if physics_state.state.on_surface().is_some() && sticky.is_some() { + vel.0 = physics_state.state.ground_vel; } tgt_pos = cpos.0; @@ -982,13 +983,13 @@ impl<'a> PhysicsData<'a> { > block_rpos.xy().map(|e| e.abs()).reduce_partial_max() { if block_rpos.z > 0.0 { - physics_state.on_ground = block.copied(); + physics_state.state.on_ground = block.copied(); } else { - physics_state.on_ceiling = true; + physics_state.state.on_ceiling = true; } vel.0.z = 0.0; } else { - physics_state.on_wall = + physics_state.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() @@ -1000,11 +1001,11 @@ impl<'a> PhysicsData<'a> { // Sticky things shouldn't move if sticky.is_some() { - vel.0 = physics_state.ground_vel; + vel.0 = physics_state.state.ground_vel; } } - physics_state.in_fluid = read + physics_state.state.in_fluid = read .terrain .get(pos.0.map(|e| e.floor() as i32)) .ok() @@ -1015,7 +1016,7 @@ impl<'a> PhysicsData<'a> { vel: Vel::zero(), }) }) - .or_else(|| match physics_state.in_fluid { + .or_else(|| match physics_state.state.in_fluid { Some(Fluid::Liquid { .. }) | None => Some(Fluid::Air { elevation: pos.0.z, vel: Vel::default(), @@ -1113,7 +1114,7 @@ impl<'a> PhysicsData<'a> { return; } - let mut physics_state_delta = physics_state.clone(); + let mut physics_state_delta = physics_state.state; // deliberately don't use scale yet here, because the // 11.0/0.8 thing is // in the comp::Scale for visual reasons @@ -1191,7 +1192,7 @@ impl<'a> PhysicsData<'a> { // based on the most // recent terrain that collision was attempted with if physics_state_delta.on_ground.is_some() { - physics_state.ground_vel = vel_other; + physics_state.state.ground_vel = vel_other; // Rotate if on ground ori = ori.rotated( @@ -1199,16 +1200,16 @@ impl<'a> PhysicsData<'a> { * previous_cache_other.ori.inverse(), ); } - physics_state.on_ground = - physics_state.on_ground.or(physics_state_delta.on_ground); - physics_state.on_ceiling |= physics_state_delta.on_ceiling; - physics_state.on_wall = physics_state.on_wall.or_else(|| { + physics_state.state.on_ground = + physics_state.state.on_ground.or(physics_state_delta.on_ground); + physics_state.state.on_ceiling |= physics_state_delta.on_ceiling; + physics_state.state.on_wall = physics_state.state.on_wall.or_else(|| { physics_state_delta .on_wall .map(|dir| ori_from.mul_direction(dir)) }); - physics_state.in_fluid = match ( - physics_state.in_fluid, + physics_state.state.in_fluid = match ( + physics_state.state.in_fluid, physics_state_delta.in_fluid, ) { (Some(x), Some(y)) => x @@ -1467,7 +1468,7 @@ fn box_voxel_collision<'a, T: BaseVol + ReadVol>( pos: &mut Pos, tgt_pos: Vec3, vel: &mut Vel, - physics_state: &mut PhysicsState, + physics_state: &mut PhysicsStateFast, ground_vel: Vec3, dt: &DeltaTime, was_on_ground: bool, diff --git a/common/systems/src/projectile.rs b/common/systems/src/projectile.rs index 503ccab63c..5767c4f57e 100644 --- a/common/systems/src/projectile.rs +++ b/common/systems/src/projectile.rs @@ -86,7 +86,7 @@ impl<'a> System<'a> for Sys { .and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into())); let mut rng = thread_rng(); - if physics.on_surface().is_none() && rng.gen_bool(0.05) { + if physics.state.on_surface().is_none() && rng.gen_bool(0.05) { server_emitter.emit(ServerEvent::Sound { sound: Sound::new(SoundKind::Projectile, pos.0, 4.0, read_data.time.0), }); @@ -162,7 +162,7 @@ impl<'a> System<'a> for Sys { } } - if physics.on_surface().is_some() { + if physics.state.on_surface().is_some() { let projectile_write = &mut *projectile_write; for effect in projectile_write.hit_solid.drain(..) { match effect { diff --git a/common/systems/src/shockwave.rs b/common/systems/src/shockwave.rs index f0ad8268b3..359141fea4 100644 --- a/common/systems/src/shockwave.rs +++ b/common/systems/src/shockwave.rs @@ -182,7 +182,7 @@ impl<'a> System<'a> for Sys { arc_strip.collides_with_circle(Disk::new(pos_b2, rad_b)) } && (pos_b_ground - pos.0).angle_between(pos_b.0 - pos.0) < max_angle - && (!shockwave.requires_ground || physics_state_b.on_ground.is_some()); + && (!shockwave.requires_ground || physics_state_b.state.on_ground.is_some()); if hit { let dir = Dir::from_unnormalized(pos_b.0 - pos.0).unwrap_or(look_dir); diff --git a/common/systems/src/stats.rs b/common/systems/src/stats.rs index 34c6da3148..f4709720b6 100644 --- a/common/systems/src/stats.rs +++ b/common/systems/src/stats.rs @@ -23,6 +23,7 @@ const POISE_REGEN_ACCEL: f32 = 2.0; #[derive(SystemData)] pub struct ReadData<'a> { entities: Entities<'a>, + stats: ReadStorage<'a, Stats>, dt: Read<'a, DeltaTime>, time: Read<'a, Time>, server_bus: Read<'a, EventBus>, @@ -39,7 +40,6 @@ pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( ReadData<'a>, - WriteStorage<'a, Stats>, WriteStorage<'a, SkillSet>, WriteStorage<'a, Health>, WriteStorage<'a, Poise>, @@ -56,7 +56,6 @@ impl<'a> System<'a> for Sys { _job: &mut Job, ( read_data, - stats, mut skill_sets, mut healths, mut poises, @@ -72,7 +71,7 @@ impl<'a> System<'a> for Sys { // Update stats for (entity, stats, mut health, pos, mut energy, inventory) in ( &read_data.entities, - &stats, + &read_data.stats, &mut healths, &read_data.positions, &mut energies, diff --git a/common/systems/tests/character_state.rs b/common/systems/tests/character_state.rs index 467308c546..28910f0c9e 100644 --- a/common/systems/tests/character_state.rs +++ b/common/systems/tests/character_state.rs @@ -3,7 +3,7 @@ mod tests { use common::{ comp::{ item::MaterialStatManifest, skills::GeneralSkill, CharacterState, Controller, Energy, - Ori, PhysicsState, Poise, Pos, Skill, Stats, Vel, + MovementState, Ori, PhysicsState, Poise, Pos, Skill, Stats, Vel, }, resources::{DeltaTime, GameMode, Time}, terrain::{MapSizeLg, TerrainChunk}, @@ -51,6 +51,7 @@ mod tests { .ecs_mut() .create_entity() .with(CharacterState::Idle(common::states::idle::Data::default())) + .with(MovementState::default()) .with(Pos(Vec3::zero())) .with(Vel::default()) .with(ori) @@ -105,11 +106,22 @@ mod tests { Ori::from_unnormalized_vec(testcases[i].0).unwrap_or_default(), )); } - tick(&mut state, Duration::from_secs_f32(0.033)); - let results = state.ecs().read_storage::(); + let dt = 0.033; + tick(&mut state, Duration::from_secs_f32(dt)); + let movement = state.ecs().read_storage::(); + let pos = state.ecs().read_storage::(); + let vel = state.ecs().read_storage::(); + let ori = state.ecs().read_storage::(); + let dt = DeltaTime(dt); for i in 0..TESTCASES { if let Some(e) = entities[i] { - let result = Dir::from(*results.get(e).expect("Ori missing")); + let mut pos = *pos.get(e).expect("Pos missing"); + let mut vel = *vel.get(e).expect("Vel missing"); + let mut ori = *ori.get(e).expect("Ori missing"); + if let Some(movement) = movement.get(e) { + movement.handle_movement(&dt, &mut pos, &mut vel, &mut ori); + } + let result = Dir::from(ori); assert!(result.abs_diff_eq(&testcases[i].1, 0.0005)); // println!("{:?}", result); } diff --git a/common/systems/tests/phys/utils.rs b/common/systems/tests/phys/utils.rs index 232eda4815..5f6cc04717 100644 --- a/common/systems/tests/phys/utils.rs +++ b/common/systems/tests/phys/utils.rs @@ -2,11 +2,12 @@ use common::{ comp::{ inventory::item::MaterialStatManifest, skills::{GeneralSkill, Skill}, - Auras, Buffs, CharacterState, Collider, Combo, Controller, Energy, Health, Ori, Pos, Stats, - Vel, + Auras, Buffs, CharacterState, Collider, Combo, Controller, Energy, Health, MovementState, + Ori, Pos, Stats, Vel, }, resources::{DeltaTime, GameMode, Time}, skillset_builder::SkillSetBuilder, + slowjob::SlowJobPool, terrain::{ Block, BlockKind, MapSizeLg, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainGrid, }, @@ -42,6 +43,10 @@ pub fn setup() -> State { Arc::new(TerrainChunk::water(0)), ); state.ecs_mut().insert(MaterialStatManifest::with_empty()); + state + .ecs_mut() + .write_resource::() + .configure("CHUNK_DROP", |_n| 1); state.ecs_mut().read_resource::