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<PhysicsState> and read deltas.
This commit is contained in:
Joshua Yanovski 2022-09-28 13:46:39 -07:00
parent 8383a72818
commit 338b377ef4
39 changed files with 185 additions and 128 deletions

View File

@ -655,7 +655,7 @@ impl CharacterAbility {
pub fn requirements_paid(&self, data: &JoinData, update: &mut StateUpdate) -> bool { pub fn requirements_paid(&self, data: &JoinData, update: &mut StateUpdate) -> bool {
match self { match self {
CharacterAbility::Roll { energy_cost, .. } => { 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 && data.inputs.move_dir.magnitude_squared() > 0.25
&& update.energy.try_change_by(-*energy_cost).is_ok() && update.energy.try_change_by(-*energy_cost).is_ok()
}, },

View File

@ -100,8 +100,8 @@ pub use self::{
ori::Ori, ori::Ori,
pet::Pet, pet::Pet,
phys::{ phys::{
Collider, Density, ForceUpdate, Immovable, Mass, PhysicsState, Pos, PosVelOriDefer, Collider, Density, ForceUpdate, Immovable, Mass, PhysicsState, PhysicsStateFast, Pos,
PreviousPhysCache, Scale, Sticky, Vel, PosVelOriDefer, PreviousPhysCache, Scale, Sticky, Vel,
}, },
player::DisconnectReason, player::DisconnectReason,
player::{AliasError, Player, MAX_ALIAS_LEN}, player::{AliasError, Player, MAX_ALIAS_LEN},

View File

@ -170,13 +170,13 @@ impl Component for Immovable {
type Storage = DerefFlaggedStorage<Self, NullStorage<Self>>; type Storage = DerefFlaggedStorage<Self, NullStorage<Self>>;
} }
// PhysicsState /// Cheaply copyable components of PhysicsState used for most delta
#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] /// computations.
pub struct PhysicsState { #[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct PhysicsStateFast {
pub on_ground: Option<Block>, pub on_ground: Option<Block>,
pub on_ceiling: bool, pub on_ceiling: bool,
pub on_wall: Option<Vec3<f32>>, pub on_wall: Option<Vec3<f32>>,
pub touch_entities: HashSet<Uid>,
pub in_fluid: Option<Fluid>, pub in_fluid: Option<Fluid>,
pub ground_vel: Vec3<f32>, pub ground_vel: Vec3<f32>,
pub footwear: Friction, pub footwear: Friction,
@ -184,6 +184,13 @@ pub struct PhysicsState {
pub skating_active: bool, pub skating_active: bool,
} }
// PhysicsState
#[derive(Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct PhysicsState {
pub state: PhysicsStateFast,
pub touch_entities: HashSet<Uid>,
}
impl PhysicsState { impl PhysicsState {
pub fn reset(&mut self) { pub fn reset(&mut self) {
// Avoid allocation overhead! // Avoid allocation overhead!
@ -191,12 +198,17 @@ impl PhysicsState {
touch_entities.clear(); touch_entities.clear();
*self = Self { *self = Self {
touch_entities, touch_entities,
ground_vel: self.ground_vel, /* Preserved, since it's the velocity of the last state: PhysicsStateFast {
* contact point */ ground_vel: self.state.ground_vel, /* Preserved, since it's the velocity of the
..Self::default() * last
* contact point */
..Default::default()
},
} }
} }
}
impl PhysicsStateFast {
pub fn on_surface(&self) -> Option<Vec3<f32>> { pub fn on_surface(&self) -> Option<Vec3<f32>> {
self.on_ground self.on_ground
.map(|_| -Vec3::unit_z()) .map(|_| -Vec3::unit_z())

View File

@ -226,6 +226,11 @@ pub enum ServerEvent {
entity: EcsEntity, entity: EcsEntity,
update: comp::MapMarkerChange, update: comp::MapMarkerChange,
}, },
/// FIXME: Remove this hack! It is only used for dousing flames.
UpdateBody {
entity: EcsEntity,
new_body: comp::Body,
},
} }
pub struct EventBus<E> { pub struct EventBus<E> {

View File

@ -164,14 +164,14 @@ impl CharacterBehavior for Data {
.prerotated(pitch) .prerotated(pitch)
}; };
// Velocity relative to the current ground // 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 // Gets offsets
let body_offsets = beam_offsets( let body_offsets = beam_offsets(
data.body, data.body,
data.inputs.look_dir, data.inputs.look_dir,
data.ori.look_vec(), data.ori.look_vec(),
rel_vel, rel_vel,
data.physics.on_ground, data.physics.state.on_ground,
); );
let pos = Pos(data.pos.0 + body_offsets); let pos = Pos(data.pos.0 + body_offsets);

View File

@ -60,9 +60,9 @@ impl CharacterBehavior for Data {
// If no wall is in front of character or we stopped climbing; // If no wall is in front of character or we stopped climbing;
let (wall_dir, climb) = if let (Some(wall_dir), Some(climb), None) = ( 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.inputs.climb,
data.physics.on_ground, data.physics.state.on_ground,
) { ) {
(wall_dir, climb) (wall_dir, climb)
} else { } else {
@ -102,7 +102,7 @@ impl CharacterBehavior for Data {
// Smooth orientation // Smooth orientation
data.ori.slerped_towards( data.ori.slerped_towards(
Ori::from(ori_dir), Ori::from(ori_dir),
if data.physics.on_ground.is_some() { if data.physics.state.on_ground.is_some() {
9.0 9.0
} else { } else {
2.0 2.0

View File

@ -19,7 +19,7 @@ impl CharacterBehavior for Data {
handle_jump(data, output_events, &mut update, 1.0); handle_jump(data, output_events, &mut update, 1.0);
// Try to Fall/Stand up/Move // 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()); update.character = CharacterState::Idle(idle::Data::default());
} }

View File

@ -79,11 +79,11 @@ impl CharacterBehavior for Data {
let mut update = StateUpdate::from(data); let mut update = StateUpdate::from(data);
// If player is on ground, end glide // If player is on ground, end glide
if data.physics.on_ground.is_some() if data.physics.state.on_ground.is_some()
&& (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2) && (data.vel.0 - data.physics.state.ground_vel).magnitude_squared() < 2_f32.powi(2)
{ {
update.character = CharacterState::GlideWield(glide_wield::Data::from(data)); 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 || data
.inventory .inventory
.and_then(|inv| inv.equipped(EquipSlot::Glider)) .and_then(|inv| inv.equipped(EquipSlot::Glider))
@ -93,6 +93,7 @@ impl CharacterBehavior for Data {
} else if !handle_climb(data, &mut update) { } else if !handle_climb(data, &mut update) {
let air_flow = data let air_flow = data
.physics .physics
.state
.in_fluid .in_fluid
.map(|fluid| fluid.relative_flow(data.vel)) .map(|fluid| fluid.relative_flow(data.vel))
.unwrap_or_default(); .unwrap_or_default();
@ -171,7 +172,7 @@ impl CharacterBehavior for Data {
Quaternion::rotation_3d( Quaternion::rotation_3d(
PI / 2.0 PI / 2.0
* accel_factor * accel_factor
* if data.physics.on_ground.is_some() { * if data.physics.state.on_ground.is_some() {
-1.0 -1.0
} else { } else {
1.0 1.0

View File

@ -49,7 +49,7 @@ impl CharacterBehavior for Data {
// If still in this state, do the things // If still in this state, do the things
if matches!(update.character, CharacterState::GlideWield(_)) { if matches!(update.character, CharacterState::GlideWield(_)) {
// If not on the ground while wielding glider enter gliding state // 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( CharacterState::Glide(glide::Data::new(
self.span_length, self.span_length,
self.chord_length, self.chord_length,
@ -60,7 +60,7 @@ impl CharacterBehavior for Data {
.inventory .inventory
.and_then(|inv| inv.equipped(EquipSlot::Glider)) .and_then(|inv| inv.equipped(EquipSlot::Glider))
.is_some() .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 { CharacterState::GlideWield(Self {
// Glider tilt follows look dir // Glider tilt follows look dir

View File

@ -30,7 +30,7 @@ impl CharacterBehavior for Data {
// Try to Fall/Stand up/Move // Try to Fall/Stand up/Move
if self.is_sneaking 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 { update.character = CharacterState::Idle(Data {
is_sneaking: false, is_sneaking: false,

View File

@ -90,7 +90,7 @@ impl CharacterBehavior for Data {
timer: tick_attack_or_default(data, self.timer, None), timer: tick_attack_or_default(data, self.timer, None),
..*self ..*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 // Transitions to swing portion of state upon hitting ground
update.character = CharacterState::LeapMelee(Data { update.character = CharacterState::LeapMelee(Data {
timer: Duration::default(), timer: Duration::default(),

View File

@ -19,7 +19,7 @@ impl CharacterBehavior for Data {
handle_jump(data, output_events, &mut update, 1.0); handle_jump(data, output_events, &mut update, 1.0);
// Try to Fall/Stand up/Move // 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()); update.character = CharacterState::Idle(idle::Data::default());
} }

View File

@ -39,7 +39,7 @@ impl CharacterBehavior for Data {
handle_wield(data, &mut update); handle_wield(data, &mut update);
handle_jump(data, output_events, &mut update, 1.0); 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 { update.character = CharacterState::Idle(idle::Data {
is_sneaking: false, is_sneaking: false,
footwear: Some(self.footwear), footwear: Some(self.footwear),

View File

@ -62,7 +62,7 @@ impl CharacterBehavior for Data {
match self.static_data.movement_behavior { match self.static_data.movement_behavior {
MovementBehavior::ForwardGround | MovementBehavior::Stationary => {}, MovementBehavior::ForwardGround | MovementBehavior::Stationary => {},
MovementBehavior::AxeHover => { 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: 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? // 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)); let dir = Dir::from_unnormalized(data.inputs.move_dir.with_z(0.0));

View File

@ -327,7 +327,7 @@ pub fn handle_skating(data: &JoinData, update: &mut StateUpdate) {
footwear, footwear,
}); });
} }
if data.physics.skating_active { if data.physics.state.skating_active {
update.character = update.character =
CharacterState::Skate(skate::Data::new(data, footwear.unwrap_or(Friction::Normal))); 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) { pub fn handle_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) {
let submersion = data let submersion = data
.physics .physics
.state
.in_liquid() .in_liquid()
.map(|depth| depth / data.body.height()); .map(|depth| depth / data.body.height());
if input_is_pressed(data, InputKind::Fly) if input_is_pressed(data, InputKind::Fly)
&& submersion.map_or(true, |sub| sub < 1.0) && 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() && data.body.fly_thrust().is_some()
{ {
fly_move(data, update, efficiency); 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()) && data.body.swim_thrust().is_some())
.then_some(submersion) .then_some(submersion)
.flatten() .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) { fn basic_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) {
let efficiency = efficiency * data.stats.move_speed_modifier * data.stats.friction_modifier; 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 // FRIC_GROUND temporarily used to normalize things around expected values
data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND
} else { } else {
@ -409,7 +410,7 @@ pub fn handle_forced_movement(
match movement { match movement {
ForcedMovement::Forward { strength } => { ForcedMovement::Forward { strength } => {
let strength = strength * data.stats.move_speed_modifier * data.stats.friction_modifier; 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 // FRIC_GROUND temporarily used to normalize things around expected values
data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND 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° // unit is multiples of 180°
let half_turns_per_tick = data.body.base_ori_rate() let half_turns_per_tick = data.body.base_ori_rate()
* efficiency * efficiency
* if data.physics.on_ground.is_some() { * if data.physics.state.on_ground.is_some() {
1.0 1.0
} else { } else {
0.2 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)) Density((update.density.0 + data.dt.0 * rate * change).clamp(min, max))
}; };
let def_density = ship.density().0; 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; let hull_density = ship.hull_density().0;
update.density.0 = update.density.0 =
regulate_density(def_density * 0.6, hull_density, hull_density, 25.0).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 /// Checks that player can `Sit` and updates `CharacterState` if so
pub fn attempt_sit(data: &JoinData<'_>, update: &mut StateUpdate) { 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; update.character = CharacterState::Sit;
} }
} }
pub fn attempt_dance(data: &JoinData<'_>, update: &mut StateUpdate) { 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; update.character = CharacterState::Dance;
} }
} }
pub fn attempt_talk(data: &JoinData<'_>, update: &mut StateUpdate) { 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; update.character = CharacterState::Talk;
} }
} }
pub fn attempt_sneak(data: &JoinData<'_>, update: &mut StateUpdate) { 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 { update.character = Idle(idle::Data {
is_sneaking: true, is_sneaking: true,
footwear: data.character.footwear(), 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 /// Checks that player can `Climb` and updates `CharacterState` if so
pub fn handle_climb(data: &JoinData<'_>, update: &mut StateUpdate) -> bool { pub fn handle_climb(data: &JoinData<'_>, update: &mut StateUpdate) -> bool {
if data.inputs.climb.is_some() if data.inputs.climb.is_some()
&& data.physics.on_wall.is_some() && data.physics.state.on_wall.is_some()
&& data.physics.on_ground.is_none() && data.physics.state.on_ground.is_none()
&& !data && !data
.physics .physics
.state
.in_liquid() .in_liquid()
.map(|depth| depth > 1.0) .map(|depth| depth > 1.0)
.unwrap_or(false) .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 { pub fn handle_wallrun(data: &JoinData<'_>, update: &mut StateUpdate) -> bool {
if data.physics.on_wall.is_some() if data.physics.state.on_wall.is_some()
&& data.physics.on_ground.is_none() && data.physics.state.on_ground.is_none()
&& data.physics.in_liquid().is_none() && data.physics.state.in_liquid().is_none()
&& data.body.can_climb() && data.body.can_climb()
{ {
update.character = CharacterState::Wallrun(wallrun::Data); update.character = CharacterState::Wallrun(wallrun::Data);
@ -895,6 +897,7 @@ pub fn attempt_glide_wield(
.is_some() .is_some()
&& !data && !data
.physics .physics
.state
.in_liquid() .in_liquid()
.map(|depth| depth > 1.0) .map(|depth| depth > 1.0)
.unwrap_or(false) .unwrap_or(false)
@ -916,7 +919,7 @@ pub fn handle_jump(
_update: &mut StateUpdate, _update: &mut StateUpdate,
strength: f32, strength: f32,
) -> bool { ) -> 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()) .then(|| data.body.jump_impulse())
.flatten() .flatten()
.map(|impulse| { .map(|impulse| {

View File

@ -33,9 +33,9 @@ impl CharacterBehavior for Data {
// fall off wall, hit ground, or enter water // fall off wall, hit ground, or enter water
// TODO: Rugged way to determine when state change occurs and we need to leave // TODO: Rugged way to determine when state change occurs and we need to leave
// this state // this state
if data.physics.on_wall.is_none() if data.physics.state.on_wall.is_none()
|| data.physics.on_ground.is_some() || data.physics.state.on_ground.is_some()
|| data.physics.in_liquid().is_some() || data.physics.state.in_liquid().is_some()
{ {
update.character = CharacterState::Idle(idle::Data::default()); update.character = CharacterState::Idle(idle::Data::default());
} }

View File

@ -28,7 +28,7 @@ impl CharacterBehavior for Data {
handle_jump(data, output_events, &mut update, 1.0); handle_jump(data, output_events, &mut update, 1.0);
if self.is_sneaking 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 }); update.character = CharacterState::Wielding(Data { is_sneaking: false });
} }
@ -96,7 +96,7 @@ impl CharacterBehavior for Data {
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate { fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data); 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.character = CharacterState::Wielding(Data { is_sneaking: true });
} }
update update

View File

@ -575,7 +575,8 @@ impl State {
match event { match event {
LocalEvent::Jump(entity, impulse) => { LocalEvent::Jump(entity, impulse) => {
if let Some(vel) = velocities.get_mut(entity) { 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 } => { LocalEvent::ApplyImpulse { entity, impulse } => {

View File

@ -31,6 +31,7 @@ pub struct ReadData<'a> {
entities: Entities<'a>, entities: Entities<'a>,
dt: Read<'a, DeltaTime>, dt: Read<'a, DeltaTime>,
server_bus: Read<'a, EventBus<ServerEvent>>, server_bus: Read<'a, EventBus<ServerEvent>>,
bodies: ReadStorage<'a, Body>,
inventories: ReadStorage<'a, Inventory>, inventories: ReadStorage<'a, Inventory>,
healths: ReadStorage<'a, Health>, healths: ReadStorage<'a, Health>,
energies: ReadStorage<'a, Energy>, energies: ReadStorage<'a, Energy>,
@ -48,7 +49,6 @@ impl<'a> System<'a> for Sys {
ReadData<'a>, ReadData<'a>,
WriteStorage<'a, Buffs>, WriteStorage<'a, Buffs>,
WriteStorage<'a, Stats>, WriteStorage<'a, Stats>,
WriteStorage<'a, Body>,
WriteStorage<'a, LightEmitter>, WriteStorage<'a, LightEmitter>,
); );
@ -58,7 +58,7 @@ impl<'a> System<'a> for Sys {
fn run( fn run(
job: &mut Job<Self>, job: &mut Job<Self>,
(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 mut server_emitter = read_data.server_bus.emitter();
let dt = read_data.dt.0; let dt = read_data.dt.0;
@ -74,7 +74,7 @@ impl<'a> System<'a> for Sys {
prof_span!(guard_, "buff campfire deactivate"); prof_span!(guard_, "buff campfire deactivate");
( (
&read_data.entities, &read_data.entities,
&mut bodies, &read_data.bodies,
&read_data.physics_states, &read_data.physics_states,
light_emitters_mask, //to improve iteration speed light_emitters_mask, //to improve iteration speed
) )
@ -82,16 +82,19 @@ impl<'a> System<'a> for Sys {
.filter(|(_, body, physics_state, _)| { .filter(|(_, body, physics_state, _)| {
matches!(&**body, Body::Object(object::Body::CampfireLit)) matches!(&**body, Body::Object(object::Body::CampfireLit))
&& matches!( && matches!(
physics_state.in_fluid, physics_state.state.in_fluid,
Some(Fluid::Liquid { Some(Fluid::Liquid {
kind: LiquidKind::Water, kind: LiquidKind::Water,
.. ..
}) })
) )
}) })
.for_each(|(e, mut body, _, _)| { .for_each(|(entity, _, _, _)| {
*body = Body::Object(object::Body::Campfire); server_emitter.emit(ServerEvent::UpdateBody {
light_emitters.remove(e); entity,
new_body: Body::Object(object::Body::Campfire),
});
light_emitters.remove(entity);
}); });
drop(guard_); drop(guard_);
@ -108,7 +111,7 @@ impl<'a> System<'a> for Sys {
// Apply buffs to entity based off of their current physics_state // Apply buffs to entity based off of their current physics_state
if let Some(physics_state) = physics_state { if let Some(physics_state) = physics_state {
if matches!( 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) Some(SpriteKind::EnsnaringVines) | Some(SpriteKind::EnsnaringWeb)
) { ) {
// If on ensnaring vines, apply ensnared debuff // If on ensnaring vines, apply ensnared debuff
@ -123,7 +126,7 @@ impl<'a> System<'a> for Sys {
}); });
} }
if matches!( 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) Some(SpriteKind::SeaUrchin)
) { ) {
// If touching Sea Urchin apply Bleeding buff // If touching Sea Urchin apply Bleeding buff
@ -138,7 +141,7 @@ impl<'a> System<'a> for Sys {
}); });
} }
if matches!( if matches!(
physics_state.in_fluid, physics_state.state.in_fluid,
Some(Fluid::Liquid { Some(Fluid::Liquid {
kind: LiquidKind::Lava, kind: LiquidKind::Lava,
.. ..
@ -155,7 +158,7 @@ impl<'a> System<'a> for Sys {
)), )),
}); });
} else if matches!( } else if matches!(
physics_state.in_fluid, physics_state.state.in_fluid,
Some(Fluid::Liquid { Some(Fluid::Liquid {
kind: LiquidKind::Water, kind: LiquidKind::Water,
.. ..

View File

@ -4,7 +4,8 @@ use common::{
fluid_dynamics::{Fluid, LiquidKind, Wings}, fluid_dynamics::{Fluid, LiquidKind, Wings},
inventory::item::armor::Friction, inventory::item::armor::Friction,
Body, CharacterState, Collider, Density, Immovable, Mass, MovementState, Ori, PhysicsState, 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}, consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
@ -388,7 +389,7 @@ impl<'a> PhysicsRead<'a> {
)| { )| {
let is_sticky = sticky.is_some(); let is_sticky = sticky.is_some();
let is_immovable = immovable.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_collision_checks = 0;
let mut entity_entity_collisions = 0; let mut entity_entity_collisions = 0;
@ -667,7 +668,7 @@ impl<'a> PhysicsData<'a> {
// Apply physics only if in a loaded chunk // Apply physics only if in a loaded chunk
if in_loaded_chunk if in_loaded_chunk
// And not already stuck on a block (e.g., for arrows) // 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 // Clamp dt to an effective 10 TPS, to prevent gravity
// from slamming the players into the floor when // 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). // to lag (as observed in the 0.9 release party).
let dt = DeltaTime(read.dt.0.min(0.1)); let dt = DeltaTime(read.dt.0.min(0.1));
match physics_state.in_fluid { match physics_state.state.in_fluid {
None => { None => {
vel.0.z -= dt.0 * GRAVITY; vel.0.z -= dt.0 * GRAVITY;
}, },
@ -814,8 +815,8 @@ impl<'a> PhysicsData<'a> {
if let Some(state) = character_state { if let Some(state) = character_state {
let footwear = state.footwear().unwrap_or(Friction::Normal); let footwear = state.footwear().unwrap_or(Friction::Normal);
if footwear != physics_state.footwear { if footwear != physics_state.state.footwear {
physics_state.footwear = footwear; physics_state.state.footwear = footwear;
} }
} }
@ -849,7 +850,7 @@ impl<'a> PhysicsData<'a> {
// velocities or entirely broken position snapping. // velocities or entirely broken position snapping.
let mut tgt_pos = pos.0 + pos_delta; 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 = let block_snap =
body.map_or(false, |b| !matches!(b, Body::Object(_) | Body::Ship(_))); body.map_or(false, |b| !matches!(b, Body::Object(_) | Body::Ship(_)));
let climbing = let climbing =
@ -876,7 +877,7 @@ impl<'a> PhysicsData<'a> {
&mut cpos, &mut cpos,
tgt_pos, tgt_pos,
&mut vel, &mut vel,
physics_state, &mut physics_state.state,
Vec3::zero(), Vec3::zero(),
&read.dt, &read.dt,
was_on_ground, was_on_ground,
@ -909,7 +910,7 @@ impl<'a> PhysicsData<'a> {
&mut cpos, &mut cpos,
tgt_pos, tgt_pos,
&mut vel, &mut vel,
physics_state, &mut physics_state.state,
Vec3::zero(), Vec3::zero(),
&read.dt, &read.dt,
was_on_ground, was_on_ground,
@ -921,8 +922,8 @@ impl<'a> PhysicsData<'a> {
); );
// Sticky things shouldn't move when on a surface // Sticky things shouldn't move when on a surface
if physics_state.on_surface().is_some() && sticky.is_some() { if physics_state.state.on_surface().is_some() && sticky.is_some() {
vel.0 = physics_state.ground_vel; vel.0 = physics_state.state.ground_vel;
} }
tgt_pos = cpos.0; tgt_pos = cpos.0;
@ -982,13 +983,13 @@ impl<'a> PhysicsData<'a> {
> block_rpos.xy().map(|e| e.abs()).reduce_partial_max() > block_rpos.xy().map(|e| e.abs()).reduce_partial_max()
{ {
if block_rpos.z > 0.0 { if block_rpos.z > 0.0 {
physics_state.on_ground = block.copied(); physics_state.state.on_ground = block.copied();
} else { } else {
physics_state.on_ceiling = true; physics_state.state.on_ceiling = true;
} }
vel.0.z = 0.0; vel.0.z = 0.0;
} else { } else {
physics_state.on_wall = physics_state.state.on_wall =
Some(if block_rpos.x.abs() > block_rpos.y.abs() { Some(if block_rpos.x.abs() > block_rpos.y.abs() {
vel.0.x = 0.0; vel.0.x = 0.0;
Vec3::unit_x() * -block_rpos.x.signum() Vec3::unit_x() * -block_rpos.x.signum()
@ -1000,11 +1001,11 @@ impl<'a> PhysicsData<'a> {
// Sticky things shouldn't move // Sticky things shouldn't move
if sticky.is_some() { 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 .terrain
.get(pos.0.map(|e| e.floor() as i32)) .get(pos.0.map(|e| e.floor() as i32))
.ok() .ok()
@ -1015,7 +1016,7 @@ impl<'a> PhysicsData<'a> {
vel: Vel::zero(), 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 { Some(Fluid::Liquid { .. }) | None => Some(Fluid::Air {
elevation: pos.0.z, elevation: pos.0.z,
vel: Vel::default(), vel: Vel::default(),
@ -1113,7 +1114,7 @@ impl<'a> PhysicsData<'a> {
return; 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 // deliberately don't use scale yet here, because the
// 11.0/0.8 thing is // 11.0/0.8 thing is
// in the comp::Scale for visual reasons // in the comp::Scale for visual reasons
@ -1191,7 +1192,7 @@ impl<'a> PhysicsData<'a> {
// based on the most // based on the most
// recent terrain that collision was attempted with // recent terrain that collision was attempted with
if physics_state_delta.on_ground.is_some() { 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 // Rotate if on ground
ori = ori.rotated( ori = ori.rotated(
@ -1199,16 +1200,16 @@ impl<'a> PhysicsData<'a> {
* previous_cache_other.ori.inverse(), * previous_cache_other.ori.inverse(),
); );
} }
physics_state.on_ground = physics_state.state.on_ground =
physics_state.on_ground.or(physics_state_delta.on_ground); physics_state.state.on_ground.or(physics_state_delta.on_ground);
physics_state.on_ceiling |= physics_state_delta.on_ceiling; physics_state.state.on_ceiling |= physics_state_delta.on_ceiling;
physics_state.on_wall = physics_state.on_wall.or_else(|| { physics_state.state.on_wall = physics_state.state.on_wall.or_else(|| {
physics_state_delta physics_state_delta
.on_wall .on_wall
.map(|dir| ori_from.mul_direction(dir)) .map(|dir| ori_from.mul_direction(dir))
}); });
physics_state.in_fluid = match ( physics_state.state.in_fluid = match (
physics_state.in_fluid, physics_state.state.in_fluid,
physics_state_delta.in_fluid, physics_state_delta.in_fluid,
) { ) {
(Some(x), Some(y)) => x (Some(x), Some(y)) => x
@ -1467,7 +1468,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
pos: &mut Pos, pos: &mut Pos,
tgt_pos: Vec3<f32>, tgt_pos: Vec3<f32>,
vel: &mut Vel, vel: &mut Vel,
physics_state: &mut PhysicsState, physics_state: &mut PhysicsStateFast,
ground_vel: Vec3<f32>, ground_vel: Vec3<f32>,
dt: &DeltaTime, dt: &DeltaTime,
was_on_ground: bool, was_on_ground: bool,

View File

@ -86,7 +86,7 @@ impl<'a> System<'a> for Sys {
.and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into())); .and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()));
let mut rng = thread_rng(); 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 { server_emitter.emit(ServerEvent::Sound {
sound: Sound::new(SoundKind::Projectile, pos.0, 4.0, read_data.time.0), 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; let projectile_write = &mut *projectile_write;
for effect in projectile_write.hit_solid.drain(..) { for effect in projectile_write.hit_solid.drain(..) {
match effect { match effect {

View File

@ -182,7 +182,7 @@ impl<'a> System<'a> for Sys {
arc_strip.collides_with_circle(Disk::new(pos_b2, rad_b)) 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 && (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 { if hit {
let dir = Dir::from_unnormalized(pos_b.0 - pos.0).unwrap_or(look_dir); let dir = Dir::from_unnormalized(pos_b.0 - pos.0).unwrap_or(look_dir);

View File

@ -23,6 +23,7 @@ const POISE_REGEN_ACCEL: f32 = 2.0;
#[derive(SystemData)] #[derive(SystemData)]
pub struct ReadData<'a> { pub struct ReadData<'a> {
entities: Entities<'a>, entities: Entities<'a>,
stats: ReadStorage<'a, Stats>,
dt: Read<'a, DeltaTime>, dt: Read<'a, DeltaTime>,
time: Read<'a, Time>, time: Read<'a, Time>,
server_bus: Read<'a, EventBus<ServerEvent>>, server_bus: Read<'a, EventBus<ServerEvent>>,
@ -39,7 +40,6 @@ pub struct Sys;
impl<'a> System<'a> for Sys { impl<'a> System<'a> for Sys {
type SystemData = ( type SystemData = (
ReadData<'a>, ReadData<'a>,
WriteStorage<'a, Stats>,
WriteStorage<'a, SkillSet>, WriteStorage<'a, SkillSet>,
WriteStorage<'a, Health>, WriteStorage<'a, Health>,
WriteStorage<'a, Poise>, WriteStorage<'a, Poise>,
@ -56,7 +56,6 @@ impl<'a> System<'a> for Sys {
_job: &mut Job<Self>, _job: &mut Job<Self>,
( (
read_data, read_data,
stats,
mut skill_sets, mut skill_sets,
mut healths, mut healths,
mut poises, mut poises,
@ -72,7 +71,7 @@ impl<'a> System<'a> for Sys {
// Update stats // Update stats
for (entity, stats, mut health, pos, mut energy, inventory) in ( for (entity, stats, mut health, pos, mut energy, inventory) in (
&read_data.entities, &read_data.entities,
&stats, &read_data.stats,
&mut healths, &mut healths,
&read_data.positions, &read_data.positions,
&mut energies, &mut energies,

View File

@ -3,7 +3,7 @@ mod tests {
use common::{ use common::{
comp::{ comp::{
item::MaterialStatManifest, skills::GeneralSkill, CharacterState, Controller, Energy, 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}, resources::{DeltaTime, GameMode, Time},
terrain::{MapSizeLg, TerrainChunk}, terrain::{MapSizeLg, TerrainChunk},
@ -51,6 +51,7 @@ mod tests {
.ecs_mut() .ecs_mut()
.create_entity() .create_entity()
.with(CharacterState::Idle(common::states::idle::Data::default())) .with(CharacterState::Idle(common::states::idle::Data::default()))
.with(MovementState::default())
.with(Pos(Vec3::zero())) .with(Pos(Vec3::zero()))
.with(Vel::default()) .with(Vel::default())
.with(ori) .with(ori)
@ -105,11 +106,22 @@ mod tests {
Ori::from_unnormalized_vec(testcases[i].0).unwrap_or_default(), Ori::from_unnormalized_vec(testcases[i].0).unwrap_or_default(),
)); ));
} }
tick(&mut state, Duration::from_secs_f32(0.033)); let dt = 0.033;
let results = state.ecs().read_storage::<Ori>(); tick(&mut state, Duration::from_secs_f32(dt));
let movement = state.ecs().read_storage::<MovementState>();
let pos = state.ecs().read_storage::<Pos>();
let vel = state.ecs().read_storage::<Vel>();
let ori = state.ecs().read_storage::<Ori>();
let dt = DeltaTime(dt);
for i in 0..TESTCASES { for i in 0..TESTCASES {
if let Some(e) = entities[i] { 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)); assert!(result.abs_diff_eq(&testcases[i].1, 0.0005));
// println!("{:?}", result); // println!("{:?}", result);
} }

View File

@ -2,11 +2,12 @@ use common::{
comp::{ comp::{
inventory::item::MaterialStatManifest, inventory::item::MaterialStatManifest,
skills::{GeneralSkill, Skill}, skills::{GeneralSkill, Skill},
Auras, Buffs, CharacterState, Collider, Combo, Controller, Energy, Health, Ori, Pos, Stats, Auras, Buffs, CharacterState, Collider, Combo, Controller, Energy, Health, MovementState,
Vel, Ori, Pos, Stats, Vel,
}, },
resources::{DeltaTime, GameMode, Time}, resources::{DeltaTime, GameMode, Time},
skillset_builder::SkillSetBuilder, skillset_builder::SkillSetBuilder,
slowjob::SlowJobPool,
terrain::{ terrain::{
Block, BlockKind, MapSizeLg, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainGrid, Block, BlockKind, MapSizeLg, SpriteKind, TerrainChunk, TerrainChunkMeta, TerrainGrid,
}, },
@ -42,6 +43,10 @@ pub fn setup() -> State {
Arc::new(TerrainChunk::water(0)), Arc::new(TerrainChunk::water(0)),
); );
state.ecs_mut().insert(MaterialStatManifest::with_empty()); state.ecs_mut().insert(MaterialStatManifest::with_empty());
state
.ecs_mut()
.write_resource::<SlowJobPool>()
.configure("CHUNK_DROP", |_n| 1);
state.ecs_mut().read_resource::<Time>(); state.ecs_mut().read_resource::<Time>();
state.ecs_mut().read_resource::<DeltaTime>(); state.ecs_mut().read_resource::<DeltaTime>();
for x in 0..2 { for x in 0..2 {
@ -117,6 +122,7 @@ pub fn create_player(state: &mut State) -> Entity {
.with(body) .with(body)
.with(Controller::default()) .with(Controller::default())
.with(CharacterState::default()) .with(CharacterState::default())
.with(MovementState::default())
.with(Buffs::default()) .with(Buffs::default())
.with(Combo::default()) .with(Combo::default())
.with(Auras::default()) .with(Auras::default())

View File

@ -966,8 +966,8 @@ impl<'a> AgentData<'a> {
controller.inputs.look_dir, controller.inputs.look_dir,
self.ori.look_vec(), self.ori.look_vec(),
// Try to match animation by getting some context // Try to match animation by getting some context
self.vel.0 - self.physics_state.ground_vel, self.vel.0 - self.physics_state.state.ground_vel,
self.physics_state.on_ground, self.physics_state.state.on_ground,
) )
}); });
let aim_to = Vec3::new( let aim_to = Vec3::new(

View File

@ -106,7 +106,7 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3<f32>)
if let Some(physics) = ecs.read_storage::<PhysicsState>().get(entity) { if let Some(physics) = ecs.read_storage::<PhysicsState>().get(entity) {
//Check if the entity is on a surface. If it is not, reduce knockback. //Check if the entity is on a surface. If it is not, reduce knockback.
let mut impulse = impulse let mut impulse = impulse
* if physics.on_surface().is_some() { * if physics.state.on_surface().is_some() {
1.0 1.0
} else { } else {
0.4 0.4
@ -1359,3 +1359,11 @@ pub fn handle_update_map_marker(
} }
} }
} }
/// FIXME: Remove this hack! It should only be used for dousing flames from
/// campfires.
pub fn handle_update_body(server: &mut Server, entity: EcsEntity, new_body: comp::Body) {
server
.state
.write_component_ignore_entity_dead(entity, new_body);
}

View File

@ -13,7 +13,7 @@ use entity_manipulation::{
handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_combo_change, handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_combo_change,
handle_delete, handle_destroy, handle_energy_change, handle_entity_attacked_hook, handle_delete, handle_destroy, handle_energy_change, handle_entity_attacked_hook,
handle_explosion, handle_health_change, handle_knockback, handle_land_on_ground, handle_parry, handle_explosion, handle_health_change, handle_knockback, handle_land_on_ground, handle_parry,
handle_poise, handle_respawn, handle_teleport_to, handle_update_map_marker, handle_poise, handle_respawn, handle_teleport_to, handle_update_body, handle_update_map_marker,
}; };
use group_manip::handle_group; use group_manip::handle_group;
use information::handle_site_info; use information::handle_site_info;
@ -287,6 +287,9 @@ impl Server {
ServerEvent::UpdateMapMarker { entity, update } => { ServerEvent::UpdateMapMarker { entity, update } => {
handle_update_map_marker(self, entity, update) handle_update_map_marker(self, entity, update)
}, },
ServerEvent::UpdateBody { entity, new_body } => {
handle_update_body(self, entity, new_body)
},
} }
} }

View File

@ -134,7 +134,7 @@ impl<'a> System<'a> for Sys {
let is_gliding = matches!( let is_gliding = matches!(
read_data.char_states.get(entity), read_data.char_states.get(entity),
Some(CharacterState::GlideWield(_) | CharacterState::Glide(_)) Some(CharacterState::GlideWield(_) | CharacterState::Glide(_))
) && physics_state.on_ground.is_none(); ) && physics_state.state.on_ground.is_none();
if let Some(pid) = agent.position_pid_controller.as_mut() { if let Some(pid) = agent.position_pid_controller.as_mut() {
pid.add_measurement(read_data.time.0, pos.0); pid.add_measurement(read_data.time.0, pos.0);
@ -150,8 +150,8 @@ impl<'a> System<'a> for Sys {
let traversal_config = TraversalConfig { let traversal_config = TraversalConfig {
node_tolerance, node_tolerance,
slow_factor, slow_factor,
on_ground: physics_state.on_ground.is_some(), on_ground: physics_state.state.on_ground.is_some(),
in_liquid: physics_state.in_liquid().is_some(), in_liquid: physics_state.state.in_liquid().is_some(),
min_tgt_dist: 1.0, min_tgt_dist: 1.0,
can_climb: body.map_or(false, Body::can_climb), can_climb: body.map_or(false, Body::can_climb),
can_fly: body.map_or(false, |b| b.fly_thrust().is_some()), can_fly: body.map_or(false, |b| b.fly_thrust().is_some()),

View File

@ -184,7 +184,7 @@ fn react_if_on_fire(bdata: &mut BehaviorData) -> bool {
if is_on_fire if is_on_fire
&& bdata.agent_data.body.map_or(false, |b| b.is_humanoid()) && bdata.agent_data.body.map_or(false, |b| b.is_humanoid())
&& bdata.agent_data.physics_state.on_ground.is_some() && bdata.agent_data.physics_state.state.on_ground.is_some()
&& bdata && bdata
.rng .rng
.gen_bool((2.0 * bdata.read_data.dt.0).clamp(0.0, 1.0) as f64) .gen_bool((2.0 * bdata.read_data.dt.0).clamp(0.0, 1.0) as f64)

View File

@ -44,7 +44,7 @@ impl<'a> System<'a> for Sys {
{ {
match object { match object {
Object::Bomb { owner } => { Object::Bomb { owner } => {
if physics.on_surface().is_some() { if physics.state.on_surface().is_some() {
server_emitter.emit(ServerEvent::Delete(entity)); server_emitter.emit(ServerEvent::Delete(entity));
server_emitter.emit(ServerEvent::Explosion { server_emitter.emit(ServerEvent::Explosion {
pos: pos.0, pos: pos.0,

View File

@ -54,7 +54,7 @@ impl<'a> System<'a> for Sys {
.filter(|(_, owner_pos, owner_physics, pet_pos)| { .filter(|(_, owner_pos, owner_physics, pet_pos)| {
// Don't teleport pets to the player if they're in the air, nobody wants // Don't teleport pets to the player if they're in the air, nobody wants
// pets to go splat :( // pets to go splat :(
owner_physics.on_ground.is_some() owner_physics.state.on_ground.is_some()
&& owner_pos.0.distance_squared(pet_pos.0) > LOST_PET_DISTANCE_THRESHOLD.powi(2) && owner_pos.0.distance_squared(pet_pos.0) > LOST_PET_DISTANCE_THRESHOLD.powi(2)
}) })
.map(|(entity, owner_pos, _, _)| (entity, *owner_pos)) .map(|(entity, owner_pos, _, _)| (entity, *owner_pos))

View File

@ -55,7 +55,7 @@ impl<'a> System<'a> for Sys {
) )
.join() .join()
{ {
if physics.map_or(true, |ps| ps.on_ground.is_some()) && velocity.0.z >= 0.0 { if physics.map_or(true, |ps| ps.state.on_ground.is_some()) && velocity.0.z >= 0.0 {
for (waypoint_pos, waypoint_area) in (&positions, &waypoint_areas).join() { for (waypoint_pos, waypoint_area) in (&positions, &waypoint_areas).join() {
if player_pos.0.distance_squared(waypoint_pos.0) if player_pos.0.distance_squared(waypoint_pos.0)
< waypoint_area.radius().powi(2) < waypoint_area.radius().powi(2)

View File

@ -706,17 +706,17 @@ fn selected_entity_window(
.spacing([40.0, 4.0]) .spacing([40.0, 4.0])
.striped(true) .striped(true)
.show(ui, |ui| { .show(ui, |ui| {
two_col_row(ui, "On Ground", physics_state.on_ground.map_or("None".to_owned(), |x| format!("{:?}", x))); two_col_row(ui, "On Ground", physics_state.state.on_ground.map_or("None".to_owned(), |x| format!("{:?}", x)));
two_col_row(ui, "On Ceiling", (if physics_state.on_ceiling { "True" } else { "False " }).to_string()); two_col_row(ui, "On Ceiling", (if physics_state.state.on_ceiling { "True" } else { "False " }).to_string());
two_col_row(ui, "On Wall", physics_state.on_wall.map_or("-".to_owned(), |x| format!("{:.1},{:.1},{:.1}", x.x, x.y, x.z ))); two_col_row(ui, "On Wall", physics_state.state.on_wall.map_or("-".to_owned(), |x| format!("{:.1},{:.1},{:.1}", x.x, x.y, x.z )));
two_col_row(ui, "Touching Entities", physics_state.touch_entities.len().to_string()); two_col_row(ui, "Touching Entities", physics_state.touch_entities.len().to_string());
two_col_row(ui, "In Fluid", match physics_state.in_fluid { two_col_row(ui, "In Fluid", match physics_state.state.in_fluid {
Some(Fluid::Air { elevation, .. }) => format!("Air (Elevation: {:.1})", elevation), Some(Fluid::Air { elevation, .. }) => format!("Air (Elevation: {:.1})", elevation),
Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth), Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth),
_ => "None".to_owned() }); _ => "None".to_owned() });
}); });
two_col_row(ui, "Footwear", match physics_state.footwear{ Friction::Ski => "Ski", Friction::Skate => "Skate", /* Friction::Snowshoe => "Snowshoe", Friction::Spikes => "Spikes", */ Friction::Normal=>"Normal",}.to_string()); two_col_row(ui, "Footwear", match physics_state.state.footwear{ Friction::Ski => "Ski", Friction::Skate => "Skate", /* Friction::Snowshoe => "Snowshoe", Friction::Spikes => "Spikes", */ Friction::Normal=>"Normal",}.to_string());
}); });
} }
}); });

View File

@ -9,7 +9,7 @@ use crate::{
}; };
use client::Client; use client::Client;
use common::{ use common::{
comp::{Body, CharacterState, PhysicsState, Pos, Vel}, comp::{Body, CharacterState, PhysicsState, PhysicsStateFast, Pos, Vel},
resources::DeltaTime, resources::DeltaTime,
terrain::{BlockKind, TerrainChunk}, terrain::{BlockKind, TerrainChunk},
vol::ReadVol, vol::ReadVol,
@ -72,6 +72,7 @@ impl EventMapper for MovementEventMapper {
.join() .join()
.filter(|(_, e_pos, ..)| (e_pos.0.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR) .filter(|(_, e_pos, ..)| (e_pos.0.distance_squared(cam_pos)) < SFX_DIST_LIMIT_SQR)
{ {
let physics = &physics.state;
if let Some(character) = character { if let Some(character) = character {
let internal_state = self.event_history.entry(entity).or_default(); let internal_state = self.event_history.entry(entity).or_default();
@ -187,7 +188,7 @@ impl MovementEventMapper {
/// `SfxEvent`'s which we attach sounds to /// `SfxEvent`'s which we attach sounds to
fn map_movement_event( fn map_movement_event(
character_state: &CharacterState, character_state: &CharacterState,
physics_state: &PhysicsState, physics_state: &PhysicsStateFast,
previous_state: &PreviousEntityState, previous_state: &PreviousEntityState,
vel: Vec3<f32>, vel: Vec3<f32>,
underfoot_block_kind: BlockKind, underfoot_block_kind: BlockKind,
@ -228,7 +229,7 @@ impl MovementEventMapper {
/// Maps a limited set of movements for other non-humanoid entities /// Maps a limited set of movements for other non-humanoid entities
fn map_non_humanoid_movement_event( fn map_non_humanoid_movement_event(
physics_state: &PhysicsState, physics_state: &PhysicsStateFast,
vel: Vec3<f32>, vel: Vec3<f32>,
underfoot_block_kind: BlockKind, underfoot_block_kind: BlockKind,
) -> SfxEvent { ) -> SfxEvent {
@ -250,7 +251,7 @@ impl MovementEventMapper {
/// Maps a limited set of movements for quadruped entities /// Maps a limited set of movements for quadruped entities
fn map_quadruped_movement_event( fn map_quadruped_movement_event(
physics_state: &PhysicsState, physics_state: &PhysicsStateFast,
vel: Vec3<f32>, vel: Vec3<f32>,
underfoot_block_kind: BlockKind, underfoot_block_kind: BlockKind,
) -> SfxEvent { ) -> SfxEvent {

View File

@ -3,7 +3,7 @@ use crate::audio::sfx::SfxEvent;
use common::{ use common::{
comp::{ comp::{
bird_large, humanoid, quadruped_medium, quadruped_small, Body, CharacterState, InputKind, bird_large, humanoid, quadruped_medium, quadruped_small, Body, CharacterState, InputKind,
Ori, PhysicsState, Ori, PhysicsStateFast,
}, },
states, states,
terrain::{Block, BlockKind}, terrain::{Block, BlockKind},
@ -94,7 +94,7 @@ fn same_previous_event_elapsed_emits() {
fn maps_idle() { fn maps_idle() {
let result = MovementEventMapper::map_movement_event( let result = MovementEventMapper::map_movement_event(
&CharacterState::Idle(states::idle::Data::default()), &CharacterState::Idle(states::idle::Data::default()),
&PhysicsState { &PhysicsStateFast {
on_ground: Some(Block::empty()), on_ground: Some(Block::empty()),
..Default::default() ..Default::default()
}, },
@ -116,7 +116,7 @@ fn maps_idle() {
fn maps_run_with_sufficient_velocity() { fn maps_run_with_sufficient_velocity() {
let result = MovementEventMapper::map_movement_event( let result = MovementEventMapper::map_movement_event(
&CharacterState::Idle(states::idle::Data::default()), &CharacterState::Idle(states::idle::Data::default()),
&PhysicsState { &PhysicsStateFast {
on_ground: Some(Block::empty()), on_ground: Some(Block::empty()),
..Default::default() ..Default::default()
}, },
@ -138,7 +138,7 @@ fn maps_run_with_sufficient_velocity() {
fn does_not_map_run_with_insufficient_velocity() { fn does_not_map_run_with_insufficient_velocity() {
let result = MovementEventMapper::map_movement_event( let result = MovementEventMapper::map_movement_event(
&CharacterState::Idle(states::idle::Data::default()), &CharacterState::Idle(states::idle::Data::default()),
&PhysicsState { &PhysicsStateFast {
on_ground: Some(Block::empty()), on_ground: Some(Block::empty()),
..Default::default() ..Default::default()
}, },
@ -193,7 +193,7 @@ fn maps_roll() {
is_sneaking: false, is_sneaking: false,
was_combo: None, was_combo: None,
}), }),
&PhysicsState { &PhysicsStateFast {
on_ground: Some(Block::empty()), on_ground: Some(Block::empty()),
..Default::default() ..Default::default()
}, },
@ -215,7 +215,7 @@ fn maps_roll() {
fn maps_land_on_ground_to_run() { fn maps_land_on_ground_to_run() {
let result = MovementEventMapper::map_movement_event( let result = MovementEventMapper::map_movement_event(
&CharacterState::Idle(states::idle::Data::default()), &CharacterState::Idle(states::idle::Data::default()),
&PhysicsState { &PhysicsStateFast {
on_ground: Some(Block::empty()), on_ground: Some(Block::empty()),
..Default::default() ..Default::default()
}, },
@ -255,7 +255,7 @@ fn maps_glide() {
#[test] #[test]
fn maps_quadrupeds_running() { fn maps_quadrupeds_running() {
let result = MovementEventMapper::map_non_humanoid_movement_event( let result = MovementEventMapper::map_non_humanoid_movement_event(
&PhysicsState { &PhysicsStateFast {
on_ground: Some(Block::empty()), on_ground: Some(Block::empty()),
..Default::default() ..Default::default()
}, },

View File

@ -767,6 +767,7 @@ impl FigureMgr {
.join() .join()
.enumerate() .enumerate()
{ {
let physics = &physics.state;
// Velocity relative to the current ground // Velocity relative to the current ground
let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel); let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel);

View File

@ -528,7 +528,7 @@ impl Scene {
let on_ground = ecs let on_ground = ecs
.read_storage::<comp::PhysicsState>() .read_storage::<comp::PhysicsState>()
.get(scene_data.viewpoint_entity) .get(scene_data.viewpoint_entity)
.map(|p| p.on_ground.is_some()); .map(|p| p.state.on_ground.is_some());
let (viewpoint_height, viewpoint_eye_height) = scene_data let (viewpoint_height, viewpoint_eye_height) = scene_data
.state .state

View File

@ -1061,6 +1061,7 @@ impl PlayState for SessionState {
let fluid = ecs let fluid = ecs
.read_storage::<comp::PhysicsState>() .read_storage::<comp::PhysicsState>()
.get(entity)? .get(entity)?
.state
.in_fluid?; .in_fluid?;
ecs.read_storage::<Vel>() ecs.read_storage::<Vel>()
.get(entity) .get(entity)
@ -1220,7 +1221,7 @@ impl PlayState for SessionState {
let in_fluid = ecs let in_fluid = ecs
.read_storage::<comp::PhysicsState>() .read_storage::<comp::PhysicsState>()
.get(entity) .get(entity)
.and_then(|state| state.in_fluid); .and_then(|state| state.state.in_fluid);
let character_state = ecs let character_state = ecs
.read_storage::<comp::CharacterState>() .read_storage::<comp::CharacterState>()
.get(entity) .get(entity)