From 7c5917e9a912492cf140ae1cb1f5348b290d0f49 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 25 Sep 2022 19:23:36 -0400 Subject: [PATCH] Removed mutable pos/vel/ori writes from character state system. Added a movementstate component. --- client/src/lib.rs | 5 + common/net/src/synced_components.rs | 11 +- common/src/comp/ability.rs | 2 +- common/src/comp/character_state.rs | 10 +- common/src/comp/mod.rs | 2 + common/src/comp/movement.rs | 140 ++++++++++++++++++++++ common/src/states/basic_beam.rs | 6 +- common/src/states/basic_ranged.rs | 2 +- common/src/states/behavior.rs | 11 +- common/src/states/blink.rs | 7 +- common/src/states/boost.rs | 20 ++-- common/src/states/charged_ranged.rs | 2 +- common/src/states/climb.rs | 40 +++---- common/src/states/glide.rs | 8 +- common/src/states/repeater_ranged.rs | 2 +- common/src/states/skate.rs | 7 +- common/src/states/spin_melee.rs | 12 +- common/src/states/utils.rs | 141 ++++++++++------------- common/src/states/wallrun.rs | 13 +-- common/state/src/state.rs | 1 + common/systems/src/character_behavior.rs | 35 +++--- common/systems/src/phys.rs | 16 ++- server/src/state_ext.rs | 5 +- 23 files changed, 324 insertions(+), 174 deletions(-) create mode 100644 common/src/comp/movement.rs diff --git a/client/src/lib.rs b/client/src/lib.rs index da318953a3..faaa2dbd97 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -40,6 +40,7 @@ use common::{ outcome::Outcome, recipe::{ComponentRecipeBook, RecipeBook}, resources::{GameMode, PlayerEntity, TimeOfDay}, + slowjob::SlowJobPool, spiral::Spiral2d, terrain::{ block::Block, map::MapConfig, neighbors, site::DungeonKindMeta, BiomeKind, SiteKindMeta, @@ -383,6 +384,10 @@ impl Client { *state.ecs_mut().write_resource() = PlayerEntity(Some(entity)); state.ecs_mut().insert(material_stats); state.ecs_mut().insert(ability_map); + state + .ecs_mut() + .write_resource::() + .configure("CHUNK_DROP", |_n| 1); let map_size = map_size_lg.chunks(); let max_height = world_map.max_height; diff --git a/common/net/src/synced_components.rs b/common/net/src/synced_components.rs index 1dfdf57da7..74f8a239b3 100644 --- a/common/net/src/synced_components.rs +++ b/common/net/src/synced_components.rs @@ -53,13 +53,14 @@ macro_rules! synced_components { // remove it from that and then see if it's used for anything else and try to move // to only being synced for the client's entity. skill_set: SkillSet, + loot_owner: LootOwner, // Synced to the client only for its own entity combo: Combo, active_abilities: ActiveAbilities, can_build: CanBuild, - loot_owner: LootOwner, + movement_state: MovementState, } }; } @@ -215,6 +216,10 @@ impl NetSync for SkillSet { const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; } +impl NetSync for LootOwner { + const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; +} + // These are synced only from the client's own entity. impl NetSync for Combo { @@ -229,6 +234,6 @@ impl NetSync for CanBuild { const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity; } -impl NetSync for LootOwner { - const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; +impl NetSync for MovementState { + const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity; } diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index a23e94bfcd..b552a8ed2d 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -675,7 +675,7 @@ impl CharacterAbility { update.energy.current() >= *energy_cost }, CharacterAbility::LeapMelee { energy_cost, .. } => { - update.vel.0.z >= 0.0 && update.energy.try_change_by(-*energy_cost).is_ok() + data.vel.0.z >= 0.0 && update.energy.try_change_by(-*energy_cost).is_ok() }, CharacterAbility::BasicAura { energy_cost, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 845af42ae2..4293f0561b 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ inventory::item::armor::Friction, item::ConsumableKind, ControlAction, Density, Energy, - InputAttr, InputKind, Ori, Pos, Vel, + InputAttr, InputKind, MovementState, }, event::{LocalEvent, ServerEvent}, states::{ @@ -19,9 +19,7 @@ use strum::Display; /// Data returned from character behavior fn's to Character Behavior System. pub struct StateUpdate { pub character: CharacterState, - pub pos: Pos, - pub vel: Vel, - pub ori: Ori, + pub movement: MovementState, pub density: Density, pub energy: Energy, pub swap_equipped_weapons: bool, @@ -48,9 +46,7 @@ impl<'a> OutputEvents<'a> { impl From<&JoinData<'_>> for StateUpdate { fn from(data: &JoinData) -> Self { StateUpdate { - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, + movement: MovementState::default(), density: *data.density, energy: *data.energy, swap_equipped_weapons: false, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 6513d3af98..54a7cf50e7 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -32,6 +32,7 @@ pub mod invite; pub mod loot_owner; #[cfg(not(target_arch = "wasm32"))] pub mod melee; #[cfg(not(target_arch = "wasm32"))] mod misc; +#[cfg(not(target_arch = "wasm32"))] mod movement; #[cfg(not(target_arch = "wasm32"))] pub mod ori; #[cfg(not(target_arch = "wasm32"))] pub mod pet; #[cfg(not(target_arch = "wasm32"))] mod phys; @@ -95,6 +96,7 @@ pub use self::{ loot_owner::LootOwner, melee::{Melee, MeleeConstructor}, misc::Object, + movement::{MovementKind, MovementState, OriUpdate}, ori::Ori, pet::Pet, phys::{ diff --git a/common/src/comp/movement.rs b/common/src/comp/movement.rs new file mode 100644 index 0000000000..51a05b3430 --- /dev/null +++ b/common/src/comp/movement.rs @@ -0,0 +1,140 @@ +use crate::{ + comp::{Ori, Pos, Vel}, + consts::GRAVITY, + resources::DeltaTime, +}; +use serde::{Deserialize, Serialize}; +use specs::{Component, DerefFlaggedStorage}; +use std::ops::Mul; +use vek::*; + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct MovementState { + kind: MovementKind, + ori: OriUpdate, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub enum MovementKind { + Stationary, + SlowFall { + lift: f32, + }, + Flight { + lift: f32, + dir: Vec2, + accel: f32, + }, + Swim { + dir: Vec3, + accel: f32, + }, + Leap { + dir: Vec2, + vertical: f32, + forward: f32, + progress: f32, + }, + Ground { + dir: Vec2, + accel: f32, + }, + Climb { + dir: Vec3, + accel: f32, + }, + Teleport { + pos: Vec3, + }, + Boost { + dir: Vec3, + accel: f32, + }, + ChangeSpeed { + speed: f32, + }, +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub enum OriUpdate { + New(Ori), + Stationary, +} + +impl MovementState { + pub fn with_movement(mut self, kind: MovementKind) -> Self { + self.kind = kind; + self + } + + pub fn with_ori_update(mut self, ori: OriUpdate) -> Self { + self.ori = ori; + self + } + + pub fn handle_movement(&self, dt: &DeltaTime, pos: &mut Pos, vel: &mut Vel, ori: &mut Ori) { + match self.kind { + MovementKind::Stationary => {}, + MovementKind::SlowFall { lift } => { + vel.0.z += dt.0 * lift; + }, + MovementKind::Flight { lift, dir, accel } => { + let dir = dir.try_normalized().unwrap_or_default(); + vel.0.z += dt.0 * lift; + vel.0 += dir * accel * dt.0; + }, + MovementKind::Swim { dir, accel } => { + let dir = dir.try_normalized().unwrap_or_default(); + vel.0 += dir * accel * dt.0; + }, + MovementKind::Leap { + dir, + vertical, + forward, + progress, + } => { + let dir = dir.try_normalized().unwrap_or_default(); + let progress = progress.clamp(0.0, 1.0); + // TODO: Make this += instead of =, will require changing magnitude of strengths + // probably, and potentially other behavior too Multiplication + // by 2 to make `progress` "average" 1.0 + vel.0 = dir.mul(forward).with_z(vertical * progress * 2.0); + }, + MovementKind::Ground { dir, accel } => { + let dir = dir.try_normalized().unwrap_or_default(); + vel.0 += dir * accel * dt.0; + }, + MovementKind::Climb { dir, accel } => { + let dir = dir.try_normalized().unwrap_or_default(); + vel.0.z += GRAVITY * dt.0; + vel.0 += dir * accel * dt.0; + }, + MovementKind::Teleport { pos: new_pos } => pos.0 = new_pos, + MovementKind::Boost { dir, accel } => { + let dir = dir.try_normalized().unwrap_or_default(); + vel.0 += dir * accel * dt.0; + }, + MovementKind::ChangeSpeed { speed } => { + vel.0 = vel.0.try_normalized().unwrap_or_default() * speed; + }, + } + + match self.ori { + OriUpdate::Stationary => {}, + OriUpdate::New(new_ori) => *ori = new_ori, + } + } +} + +impl Default for MovementState { + fn default() -> Self { + Self { + kind: MovementKind::Stationary, + ori: OriUpdate::Stationary, + } + } +} + +impl Component for MovementState { + type Storage = DerefFlaggedStorage; +} diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index 9e10dd3254..65243eb6c9 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -157,8 +157,8 @@ impl CharacterBehavior for Data { let pitch = xy_dir.rotation_between(look_dir); Ori::from(Vec3::new( - update.ori.look_vec().x, - update.ori.look_vec().y, + data.ori.look_vec().x, + data.ori.look_vec().y, 0.0, )) .prerotated(pitch) @@ -169,7 +169,7 @@ impl CharacterBehavior for Data { let body_offsets = beam_offsets( data.body, data.inputs.look_dir, - update.ori.look_vec(), + data.ori.look_vec(), rel_vel, data.physics.on_ground, ); diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index cc08ad9f9f..9f7b94a0ee 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -88,7 +88,7 @@ impl CharacterBehavior for Data { // Shoots all projectiles simultaneously for i in 0..self.static_data.num_projectiles { // Gets offsets - let body_offsets = data.body.projectile_offsets(update.ori.look_vec()); + let body_offsets = data.body.projectile_offsets(data.ori.look_vec()); let pos = Pos(data.pos.0 + body_offsets); // Adds a slight spread to the projectiles. First projectile has no spread, // and spread increases linearly with number of projectiles created. diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index cfde4af106..52d11218fa 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -3,7 +3,7 @@ use crate::{ self, character_state::OutputEvents, item::MaterialStatManifest, ActiveAbilities, Beam, Body, CharacterState, Combo, ControlAction, Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory, InventoryAction, Mass, Melee, Ori, PhysicsState, - Pos, SkillSet, StateUpdate, Stats, Vel, + Pos, SkillSet, StateUpdate, Stats, Vel, MovementState, }, link::Is, mounting::Rider, @@ -118,6 +118,7 @@ pub struct JoinData<'a> { pub pos: &'a Pos, pub vel: &'a Vel, pub ori: &'a Ori, + pub movement: &'a MovementState, pub mass: &'a Mass, pub density: &'a Density, pub dt: &'a DeltaTime, @@ -144,9 +145,10 @@ pub struct JoinStruct<'a> { pub entity: Entity, pub uid: &'a Uid, pub char_state: FlaggedAccessMut<'a, &'a mut CharacterState, CharacterState>, - pub pos: &'a mut Pos, - pub vel: &'a mut Vel, - pub ori: &'a mut Ori, + pub pos: &'a Pos, + pub vel: &'a Vel, + pub ori: &'a Ori, + pub movement: &'a mut MovementState, pub mass: &'a Mass, pub density: FlaggedAccessMut<'a, &'a mut Density, Density>, pub energy: FlaggedAccessMut<'a, &'a mut Energy, Energy>, @@ -180,6 +182,7 @@ impl<'a> JoinData<'a> { pos: j.pos, vel: j.vel, ori: j.ori, + movement: j.movement, mass: j.mass, density: &j.density, energy: &j.energy, diff --git a/common/src/states/blink.rs b/common/src/states/blink.rs index 7d5f5c1b8a..c693fbae3c 100644 --- a/common/src/states/blink.rs +++ b/common/src/states/blink.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{character_state::OutputEvents, CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, StateUpdate, MovementKind}, event::ServerEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -59,9 +59,10 @@ impl CharacterBehavior for Data { max_range: Some(self.static_data.max_range), }); } else if let Some(pos) = input_attr.select_pos { - update.pos.0 = pos; + update.movement = update.movement.with_movement(MovementKind::Teleport { pos }); } else { - update.pos.0 += *data.inputs.look_dir * 25.0; + let pos = data.pos.0 + *data.inputs.look_dir * 25.0; + update.movement = update.movement.with_movement(MovementKind::Teleport { pos }); } } // Transitions to recover section of stage diff --git a/common/src/states/boost.rs b/common/src/states/boost.rs index ec45f71b8b..431e96ee5b 100644 --- a/common/src/states/boost.rs +++ b/common/src/states/boost.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{character_state::OutputEvents, CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, StateUpdate, MovementKind}, states::{ behavior::{CharacterBehavior, JoinData}, utils::*, @@ -8,6 +8,7 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use std::time::Duration; +use vek::*; /// Separated out to condense update portions of character state #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -36,11 +37,12 @@ impl CharacterBehavior for Data { if self.timer < self.static_data.movement_duration { // Movement - if self.static_data.only_up { - update.vel.0.z += self.static_data.speed * data.dt.0; + let dir = if self.static_data.only_up { + Vec3::unit_z() } else { - update.vel.0 += *data.inputs.look_dir * self.static_data.speed * data.dt.0; - } + *data.inputs.look_dir + }; + update.movement = update.movement.with_movement(MovementKind::Boost { dir, accel: self.static_data.speed }); update.character = CharacterState::Boost(Data { timer: tick_attack_or_default(data, self.timer, None), ..*self @@ -50,12 +52,8 @@ impl CharacterBehavior for Data { if input_is_pressed(data, self.static_data.ability_info.input) { reset_state(self, data, output_events, &mut update); } else { - update.vel.0 = update.vel.0.try_normalized().unwrap_or_default() - * update - .vel - .0 - .magnitude() - .min(self.static_data.max_exit_velocity); + let speed = data.vel.0.magnitude().min(self.static_data.max_exit_velocity); + update.movement = update.movement.with_movement(MovementKind::ChangeSpeed { speed }); update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false }); } } diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index ac94a19035..3928a480e8 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -112,7 +112,7 @@ impl CharacterBehavior for Data { get_crit_data(data, self.static_data.ability_info); let buff_strength = get_buff_strength(data, self.static_data.ability_info); // Gets offsets - let body_offsets = data.body.projectile_offsets(update.ori.look_vec()); + let body_offsets = data.body.projectile_offsets(data.ori.look_vec()); let pos = Pos(data.pos.0 + body_offsets); let projectile = arrow.create_projectile( Some(*data.uid), diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 68c2dd0be0..4028f29377 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -2,9 +2,8 @@ use crate::{ comp::{ character_state::OutputEvents, skills::{ClimbSkill::*, Skill, SKILL_MODIFIERS}, - CharacterState, Climb, InputKind, Ori, StateUpdate, + CharacterState, Climb, InputKind, Ori, StateUpdate, MovementKind, OriUpdate, }, - consts::GRAVITY, event::LocalEvent, states::{ behavior::{CharacterBehavior, JoinData}, @@ -82,14 +81,6 @@ impl CharacterBehavior for Data { update.character = CharacterState::Idle(idle::Data::default()); return update; }; - // Move player - update.vel.0 += Vec2::broadcast(data.dt.0) - * data.inputs.move_dir - * if update.vel.0.magnitude_squared() < self.static_data.movement_speed.powi(2) { - self.static_data.movement_speed.powi(2) - } else { - 0.0 - }; // Expend energy if climbing let energy_use = match climb { @@ -107,28 +98,33 @@ impl CharacterBehavior for Data { } // Set orientation direction based on wall direction - if let Some(ori_dir) = Dir::from_unnormalized(Vec2::from(wall_dir).into()) { + let new_ori = if let Some(ori_dir) = Dir::from_unnormalized(Vec2::from(wall_dir).into()) { // Smooth orientation - update.ori = update.ori.slerped_towards( + data.ori.slerped_towards( Ori::from(ori_dir), if data.physics.on_ground.is_some() { 9.0 } else { 2.0 } * data.dt.0, - ); + ) + } else { + *data.ori }; // Apply Vertical Climbing Movement - match climb { - Climb::Down => { - update.vel.0.z += data.dt.0 * (GRAVITY - self.static_data.movement_speed.powi(2)) - }, - Climb::Up => { - update.vel.0.z += data.dt.0 * (GRAVITY + self.static_data.movement_speed.powi(2)) - }, - Climb::Hold => update.vel.0.z += data.dt.0 * GRAVITY, - } + let dir = data.inputs.move_dir.with_z(match climb { + Climb::Down => -1.0, + Climb::Up => 1.0, + Climb::Hold => 0.0, + }); + let accel = if data.vel.0.magnitude_squared() < self.static_data.movement_speed.powi(2) { + self.static_data.movement_speed.powi(2) + } else { + 0.0 + }; + + update.movement = update.movement.with_movement(MovementKind::Climb { dir, accel }).with_ori_update(OriUpdate::New(new_ori)); update } diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 81f437a198..02a68c4c57 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -2,7 +2,7 @@ use super::utils::handle_climb; use crate::{ comp::{ character_state::OutputEvents, fluid_dynamics::angle_of_attack, inventory::slot::EquipSlot, - CharacterState, Ori, StateUpdate, Vel, + CharacterState, Ori, StateUpdate, Vel, OriUpdate, }, event::LocalEvent, outcome::Outcome, @@ -144,7 +144,7 @@ impl CharacterBehavior for Data { .unwrap_or_else(|| self.ori.slerped_towards(self.ori.uprighted(), slerp_s)) }; - update.ori = { + update.movement = update.movement.with_ori_update(OriUpdate::New({ let slerp_s = { let angle = data.ori.look_dir().angle_between(*data.inputs.look_dir); let rate = 0.2 * data.body.base_ori_rate() * PI / angle; @@ -183,12 +183,12 @@ impl CharacterBehavior for Data { ) }; - update.ori.slerped_towards( + data.ori.slerped_towards( ori.to_horizontal() .prerotated(rot_from_drag * rot_from_accel), slerp_s, ) - }; + })); update.character = CharacterState::Glide(Self { ori, last_vel: *data.vel, diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index 131f4f5ed6..96d440c07d 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -93,7 +93,7 @@ impl CharacterBehavior for Data { get_crit_data(data, self.static_data.ability_info); let buff_strength = get_buff_strength(data, self.static_data.ability_info); // Gets offsets - let body_offsets = data.body.projectile_offsets(update.ori.look_vec()); + let body_offsets = data.body.projectile_offsets(data.ori.look_vec()); let pos = Pos(data.pos.0 + body_offsets); let projectile = self.static_data.projectile.create_projectile( Some(*data.uid), diff --git a/common/src/states/skate.rs b/common/src/states/skate.rs index c78e842de0..385b59e291 100644 --- a/common/src/states/skate.rs +++ b/common/src/states/skate.rs @@ -2,7 +2,7 @@ use super::utils::*; use crate::{ comp::{ character_state::OutputEvents, item::armor::Friction, CharacterState, InventoryAction, - StateUpdate, + StateUpdate, MovementKind, OriUpdate, }, states::{ behavior::{CharacterBehavior, JoinData}, @@ -46,7 +46,7 @@ impl CharacterBehavior for Data { } else { let plane_ori = data.inputs.look_dir.xy(); let orthogonal = vek::Vec2::new(plane_ori.y, -plane_ori.x); - update.ori = vek::Vec3::new(plane_ori.x, plane_ori.y, 0.0).into(); + let new_ori = vek::Vec3::new(plane_ori.x, plane_ori.y, 0.0).into(); let current_planar_velocity = data.vel.0.xy().magnitude(); let long_input = data.inputs.move_dir.dot(plane_ori); let lat_input = data.inputs.move_dir.dot(orthogonal); @@ -79,8 +79,7 @@ impl CharacterBehavior for Data { if let CharacterState::Skate(skate_data) = &mut update.character { skate_data.turn = orthogonal.dot(data.vel.0.xy()); } - let delta_vel = acceleration * data.inputs.move_dir; - update.vel.0 += vek::Vec3::new(delta_vel.x, delta_vel.y, 0.0); + update.movement = update.movement.with_movement(MovementKind::Ground { dir: data.inputs.move_dir, accel: acceleration }).with_ori_update(OriUpdate::New(new_ori)); } update diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index a7bdf86280..10f21834a0 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate, MovementKind}, consts::GRAVITY, states::{ behavior::{CharacterBehavior, JoinData}, @@ -9,7 +9,6 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use std::time::Duration; -use vek::Vec3; /// Separated out to condense update portions of character state #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -62,8 +61,13 @@ impl CharacterBehavior for Data { match self.static_data.movement_behavior { MovementBehavior::ForwardGround | MovementBehavior::Stationary => {}, MovementBehavior::AxeHover => { - let new_vel_z = update.vel.0.z + GRAVITY * data.dt.0 * 0.5; - update.vel.0 = Vec3::new(0.0, 0.0, new_vel_z) + data.inputs.move_dir * 5.0; + update.movement = update.movement.with_movement(if data.physics.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? + MovementKind::Ground { dir: data.inputs.move_dir, accel: 5.0 } + } else { + MovementKind::SlowFall { lift: GRAVITY * 0.5 } + }); }, MovementBehavior::Walking => { handle_move(data, &mut update, 0.2); diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 17dd433e61..8a3283be64 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -9,7 +9,7 @@ use crate::{ quadruped_low, quadruped_medium, quadruped_small, skills::{Skill, SwimSkill, SKILL_MODIFIERS}, theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind, - InventoryAction, StateUpdate, + InventoryAction, StateUpdate, MovementKind, OriUpdate, }, consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE}, event::{LocalEvent, ServerEvent}, @@ -370,34 +370,34 @@ fn basic_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) { } * efficiency; // Should ability to backpedal be separate from ability to strafe? - update.vel.0 += Vec2::broadcast(data.dt.0) - * accel - * if data.body.can_strafe() { - data.inputs.move_dir - * if is_strafing(data, update) { - Lerp::lerp( - Vec2::from(update.ori) - .try_normalized() - .unwrap_or_else(Vec2::zero) - .dot( - data.inputs - .move_dir - .try_normalized() - .unwrap_or_else(Vec2::zero), - ) - .add(1.0) - .div(2.0) - .max(0.0), - 1.0, - data.body.reverse_move_factor(), - ) - } else { - 1.0 - } - } else { - let fw = Vec2::from(update.ori); - fw * data.inputs.move_dir.dot(fw).max(0.0) - }; + let dir = if data.body.can_strafe() { + data.inputs.move_dir + } else { + Vec2::from(*data.ori) + }; + let accel_mod = if is_strafing(data, update) { + Lerp::lerp( + Vec2::from(*data.ori) + .try_normalized() + .unwrap_or_else(Vec2::zero) + .dot( + data.inputs + .move_dir + .try_normalized() + .unwrap_or_else(Vec2::zero), + ) + .add(1.0) + .div(2.0) + .max(0.0), + 1.0, + data.body.reverse_move_factor(), + ) + } else { + data.inputs.move_dir.dot(dir).max(0.0) + }; + let accel = accel * accel_mod; + + update.movement = update.movement.with_movement(MovementKind::Ground { dir, accel }); } /// Handles forced movement @@ -413,10 +413,11 @@ pub fn handle_forced_movement( // FRIC_GROUND temporarily used to normalize things around expected values data.body.base_accel() * block.get_traction() * block.get_friction() / FRIC_GROUND }) { - update.vel.0 += Vec2::broadcast(data.dt.0) - * accel - * (data.inputs.move_dir * 0.5 + Vec2::from(update.ori) * 1.5) - * strength; + // TODO: Remove * 2.0 with changes made in sword branch, added as hack for now to keep same behavior + let accel = accel * strength * 2.0; + // TODO: Remove move_dir portion with changes made in sword branch, added as hack for now to keep same behavior + let dir = data.inputs.move_dir * 0.5 + Vec2::from(*data.ori) * 1.5; + update.movement = update.movement.with_movement(MovementKind::Ground { dir, accel }); } }, ForcedMovement::Leap { @@ -426,26 +427,12 @@ pub fn handle_forced_movement( direction, } => { let dir = direction.get_2d_dir(data); - // Apply jumping force - update.vel.0 = Vec3::new( - dir.x, - dir.y, - vertical, - ) - // Multiply decreasing amount linearly over time (with average of 1) - * 2.0 * progress - // Apply direction - + Vec3::from(dir) - // Multiply by forward leap strength - * forward - // Control forward movement based on look direction. - // This allows players to stop moving forward when they - // look downward at target - * (1.0 - data.inputs.look_dir.z.abs()); - }, - ForcedMovement::Hover { move_input } => { - update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0) - + move_input * data.inputs.move_dir.try_normalized().unwrap_or_default(); + // Control forward movement based on look direction. + // This allows players to stop moving forward when they + // look downward at target + let forward = forward * (1.0 - data.inputs.look_dir.z.abs()); + + update.movement = update.movement.with_movement(MovementKind::Leap { dir, vertical, forward, progress }); }, } } @@ -494,22 +481,24 @@ pub fn handle_orientation( } * data.dt.0; // very rough guess - let ticks_from_target_guess = ori_absdiff(&update.ori, &target_ori) / half_turns_per_tick; + let ticks_from_target_guess = ori_absdiff(data.ori, &target_ori) / half_turns_per_tick; let instantaneous = ticks_from_target_guess < 1.0; - update.ori = if instantaneous { + let new_ori = if instantaneous { target_ori } else { let target_fraction = { // Angle factor used to keep turning rate approximately constant by // counteracting slerp turning more with a larger angle - let angle_factor = 2.0 / (1.0 - update.ori.dot(target_ori)).sqrt(); + let angle_factor = 2.0 / (1.0 - data.ori.dot(target_ori)).sqrt(); half_turns_per_tick * angle_factor }; - update + data .ori .slerped_towards(target_ori, target_fraction.min(1.0)) }; + + update.movement = update.movement.with_ori_update(OriUpdate::New(new_ori)); } /// Updates components to move player as if theyre swimming @@ -532,7 +521,7 @@ fn swim_move( let dir = if data.body.can_strafe() { data.inputs.move_dir } else { - let fw = Vec2::from(update.ori); + let fw = Vec2::from(*data.ori); fw * data.inputs.move_dir.dot(fw).max(0.0) }; @@ -543,12 +532,9 @@ fn swim_move( data.inputs.move_z }; - update.vel.0 += Vec3::broadcast(data.dt.0) - * Vec3::new(dir.x, dir.y, move_z) - .try_normalized() - .unwrap_or_default() - * water_accel - * (submersion - 0.2).clamp(0.0, 1.0).powi(2); + let dir = Vec3::new(dir.x, dir.y, move_z); + let accel = water_accel * (submersion - 0.2).clamp(0.0, 1.0).powi(2); + update.movement = update.movement.with_movement(MovementKind::Swim { dir, accel }); true } else { @@ -575,11 +561,11 @@ pub fn fly_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) handle_orientation(data, update, efficiency, None); // Elevation control - match data.body { + let lift = match data.body { // flappy flappy Body::Dragon(_) | Body::BirdLarge(_) | Body::BirdMedium(_) => { let anti_grav = GRAVITY * (1.0 + data.inputs.move_z.min(0.0)); - update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0)); + anti_grav + accel * data.inputs.move_z.max(0.0) }, // floaty floaty Body::Ship(ship) if ship.can_fly() => { @@ -601,20 +587,22 @@ pub fn fly_move(data: &JoinData<'_>, update: &mut StateUpdate, efficiency: f32) update.density.0 = regulate_density(def_density * 0.5, def_density * 1.5, def_density, 0.5).0; }; + // TODO: Make ships actually specify a lift instead of hacking density + 0.0 }, // oopsie woopsie // TODO: refactor to make this state impossible - _ => {}, + _ => 0.0, }; - update.vel.0 += Vec2::broadcast(data.dt.0) - * accel - * if data.body.can_strafe() { - data.inputs.move_dir - } else { - let fw = Vec2::from(update.ori); - fw * data.inputs.move_dir.dot(fw).max(0.0) - }; + let dir = if data.body.can_strafe() { + data.inputs.move_dir + } else { + let fw = Vec2::from(*data.ori); + fw * data.inputs.move_dir.dot(fw).max(0.0) + }; + + update.movement = update.movement.with_movement(MovementKind::Flight { lift, dir, accel }); true } else { @@ -1195,9 +1183,6 @@ pub enum ForcedMovement { progress: f32, direction: MovementDirection, }, - Hover { - move_input: f32, - }, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] diff --git a/common/src/states/wallrun.rs b/common/src/states/wallrun.rs index adbba0adee..0d0d8279f3 100644 --- a/common/src/states/wallrun.rs +++ b/common/src/states/wallrun.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{character_state::OutputEvents, CharacterState, StateUpdate}, + comp::{character_state::OutputEvents, CharacterState, StateUpdate, MovementKind}, states::{ behavior::{CharacterBehavior, JoinData}, idle, @@ -23,12 +23,11 @@ impl CharacterBehavior for Data { handle_climb(data, &mut update); { - let lift = WALLRUN_ANTIGRAV; - update.vel.0.z += data.dt.0 - * lift - * (Vec2::::from(update.vel.0).magnitude() * 0.075) - .min(1.0) - .max(0.2); + let lift = WALLRUN_ANTIGRAV + * (Vec2::::from(data.vel.0).magnitude() * 0.075) + .min(1.0) + .max(0.2); + update.movement = update.movement.with_movement(MovementKind::SlowFall { lift }); } // fall off wall, hit ground, or enter water diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 3710555517..ab501b9b44 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -178,6 +178,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Register components send from clients -> server ecs.register::(); diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index af268fb325..596c6ff20d 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -7,8 +7,8 @@ use common::{ comp::{ self, character_state::OutputEvents, inventory::item::MaterialStatManifest, ActiveAbilities, Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health, - Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, SkillSet, - StateUpdate, Stats, Vel, + Inventory, InventoryManip, Mass, Melee, MovementState, Ori, PhysicsState, Poise, Pos, + SkillSet, StateUpdate, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, link::Is, @@ -48,6 +48,9 @@ pub struct ReadData<'a> { alignments: ReadStorage<'a, comp::Alignment>, terrain: ReadExpect<'a, TerrainGrid>, inventories: ReadStorage<'a, Inventory>, + positions: ReadStorage<'a, Pos>, + velocities: ReadStorage<'a, Vel>, + orientations: ReadStorage<'a, Ori>, } /// ## Character Behavior System @@ -60,13 +63,11 @@ impl<'a> System<'a> for Sys { type SystemData = ( ReadData<'a>, WriteStorage<'a, CharacterState>, - WriteStorage<'a, Pos>, - WriteStorage<'a, Vel>, - WriteStorage<'a, Ori>, WriteStorage<'a, Density>, WriteStorage<'a, Energy>, WriteStorage<'a, Controller>, WriteStorage<'a, Poise>, + WriteStorage<'a, MovementState>, Read<'a, EventBus>, ); @@ -79,13 +80,11 @@ impl<'a> System<'a> for Sys { ( read_data, mut character_states, - mut positions, - mut velocities, - mut orientations, mut densities, mut energies, mut controllers, mut poises, + mut movements, outcomes, ): Self::SystemData, ) { @@ -101,9 +100,7 @@ impl<'a> System<'a> for Sys { entity, uid, mut char_state, - pos, - vel, - ori, + (pos, vel, ori, mut movement), mass, density, energy, @@ -118,9 +115,12 @@ impl<'a> System<'a> for Sys { &read_data.entities, &read_data.uids, &mut character_states, - &mut positions, - &mut velocities, - &mut orientations, + ( + &read_data.positions, + &read_data.velocities, + &read_data.orientations, + &mut movements, + ), &read_data.masses, &mut densities, &mut energies, @@ -179,6 +179,7 @@ impl<'a> System<'a> for Sys { pos, vel, ori, + movement: &mut movement, mass, density, energy, @@ -257,9 +258,9 @@ impl Sys { }; // These components use a different type of change detection. - *join.pos = state_update.pos; - *join.vel = state_update.vel; - *join.ori = state_update.ori; + // TODO: Would the above comment also apply to movement state? It used to apply + // to pos/vel/ori + *join.movement = state_update.movement; join.controller .queued_inputs diff --git a/common/systems/src/phys.rs b/common/systems/src/phys.rs index dbec8a23fa..fa313808de 100644 --- a/common/systems/src/phys.rs +++ b/common/systems/src/phys.rs @@ -3,8 +3,8 @@ use common::{ body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST}, fluid_dynamics::{Fluid, LiquidKind, Wings}, inventory::item::armor::Friction, - Body, CharacterState, Collider, Density, Immovable, Mass, Ori, PhysicsState, Pos, - PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel, + Body, CharacterState, Collider, Density, Immovable, Mass, MovementState, Ori, PhysicsState, + Pos, PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel, }, consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY}, event::{EventBus, ServerEvent}, @@ -124,6 +124,7 @@ pub struct PhysicsRead<'a> { densities: ReadStorage<'a, Density>, stats: ReadStorage<'a, Stats>, outcomes: Read<'a, EventBus>, + movements: ReadStorage<'a, MovementState>, } #[derive(SystemData)] @@ -1357,6 +1358,17 @@ impl<'a> System<'a> for Sys { ref mut write, } = physics_data; + ( + &read.movements, + &mut write.positions, + &mut write.velocities, + &mut write.orientations, + ) + .par_join() + .for_each(|(movement, pos, vel, ori)| { + movement.handle_movement(&read.dt, pos, vel, ori); + }); + let (spatial_grid, voxel_collider_spatial_grid) = rayon::join( || { let (spatial_grid, ()) = rayon::join( diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index e7663d9eca..a6ef602f57 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -272,6 +272,7 @@ impl StateExt for State { .with(comp::Buffs::default()) .with(comp::Combo::default()) .with(comp::Auras::default()) + .with(comp::MovementState::default()) } fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder { @@ -330,7 +331,8 @@ impl StateExt for State { .with(comp::Stats::new("Airship".to_string())) .with(comp::SkillSet::default()) .with(comp::ActiveAbilities::default()) - .with(comp::Combo::default()); + .with(comp::Combo::default()) + .with(comp::MovementState::default()); if mountable { // TODO: Re-add mounting check @@ -535,6 +537,7 @@ impl StateExt for State { self.write_component_ignore_entity_dead(entity, comp::Buffs::default()); self.write_component_ignore_entity_dead(entity, comp::Auras::default()); self.write_component_ignore_entity_dead(entity, comp::Combo::default()); + self.write_component_ignore_entity_dead(entity, comp::MovementState::default()); // Make sure physics components are updated self.write_component_ignore_entity_dead(entity, comp::ForceUpdate::forced());