mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Removed mutable pos/vel/ori writes from character state system. Added a movementstate component.
This commit is contained in:
parent
cead27989b
commit
7c5917e9a9
@ -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::<SlowJobPool>()
|
||||
.configure("CHUNK_DROP", |_n| 1);
|
||||
|
||||
let map_size = map_size_lg.chunks();
|
||||
let max_height = world_map.max_height;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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::{
|
||||
|
140
common/src/comp/movement.rs
Normal file
140
common/src/comp/movement.rs
Normal file
@ -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<f32>,
|
||||
accel: f32,
|
||||
},
|
||||
Swim {
|
||||
dir: Vec3<f32>,
|
||||
accel: f32,
|
||||
},
|
||||
Leap {
|
||||
dir: Vec2<f32>,
|
||||
vertical: f32,
|
||||
forward: f32,
|
||||
progress: f32,
|
||||
},
|
||||
Ground {
|
||||
dir: Vec2<f32>,
|
||||
accel: f32,
|
||||
},
|
||||
Climb {
|
||||
dir: Vec3<f32>,
|
||||
accel: f32,
|
||||
},
|
||||
Teleport {
|
||||
pos: Vec3<f32>,
|
||||
},
|
||||
Boost {
|
||||
dir: Vec3<f32>,
|
||||
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<Self>;
|
||||
}
|
@ -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,
|
||||
);
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)]
|
||||
|
@ -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::<f32>::from(update.vel.0).magnitude() * 0.075)
|
||||
.min(1.0)
|
||||
.max(0.2);
|
||||
let lift = WALLRUN_ANTIGRAV
|
||||
* (Vec2::<f32>::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
|
||||
|
@ -178,6 +178,7 @@ impl State {
|
||||
ecs.register::<comp::BeamSegment>();
|
||||
ecs.register::<comp::Alignment>();
|
||||
ecs.register::<comp::LootOwner>();
|
||||
ecs.register::<comp::MovementState>();
|
||||
|
||||
// Register components send from clients -> server
|
||||
ecs.register::<comp::Controller>();
|
||||
|
@ -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<Outcome>>,
|
||||
);
|
||||
|
||||
@ -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
|
||||
|
@ -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<Outcome>>,
|
||||
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(
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user