mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Move from state components to single CharaterState struct
This makes split animations easy and improves overall code quality
This commit is contained in:
parent
2211f79d28
commit
5d5ccd7b99
@ -444,12 +444,12 @@ impl Client {
|
|||||||
self.state.write_component(entity, ori);
|
self.state.write_component(entity, ori);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerMsg::EntityActionState {
|
ServerMsg::EntityCharacterState {
|
||||||
entity,
|
entity,
|
||||||
action_state,
|
character_state,
|
||||||
} => {
|
} => {
|
||||||
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
if let Some(entity) = self.state.ecs().entity_from_uid(entity) {
|
||||||
self.state.write_component(entity, action_state);
|
self.state.write_component(entity, character_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ServerMsg::InventoryUpdate(inventory) => {
|
ServerMsg::InventoryUpdate(inventory) => {
|
||||||
|
@ -188,8 +188,8 @@ impl Asset for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lazy static to find and cache where the asset directory is.
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
/// Lazy static to find and cache where the asset directory is.
|
||||||
static ref ASSETS_PATH: PathBuf = {
|
static ref ASSETS_PATH: PathBuf = {
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
use specs::{Component, FlaggedStorage, HashMapStorage};
|
|
||||||
use specs_idvs::IDVStorage;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct ActionState {
|
|
||||||
pub moving: bool,
|
|
||||||
pub on_ground: bool,
|
|
||||||
pub attacking: bool,
|
|
||||||
pub rolling: bool,
|
|
||||||
pub gliding: bool,
|
|
||||||
pub wielding: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ActionState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
moving: false,
|
|
||||||
on_ground: false,
|
|
||||||
attacking: false,
|
|
||||||
rolling: false,
|
|
||||||
gliding: false,
|
|
||||||
wielding: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Component for ActionState {
|
|
||||||
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
|
|
||||||
}
|
|
68
common/src/comp/character_state.rs
Normal file
68
common/src/comp/character_state.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use specs::{Component, FlaggedStorage, HashMapStorage};
|
||||||
|
use specs_idvs::IDVStorage;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum MovementState {
|
||||||
|
Stand,
|
||||||
|
Run,
|
||||||
|
Jump,
|
||||||
|
Glide,
|
||||||
|
Roll { time_left: Duration },
|
||||||
|
//Swim,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MovementState {
|
||||||
|
pub fn is_roll(&self) -> bool {
|
||||||
|
if let Self::Roll { .. } = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ActionState {
|
||||||
|
Idle,
|
||||||
|
Wield { time_left: Duration },
|
||||||
|
Attack { time_left: Duration, applied: bool },
|
||||||
|
//Carry,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActionState {
|
||||||
|
pub fn is_wield(&self) -> bool {
|
||||||
|
if let Self::Wield { .. } = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_attack(&self) -> bool {
|
||||||
|
if let Self::Attack { .. } = self {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct CharacterState {
|
||||||
|
pub movement: MovementState,
|
||||||
|
pub action: ActionState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CharacterState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
movement: MovementState::Jump,
|
||||||
|
action: ActionState::Idle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for CharacterState {
|
||||||
|
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
mod action_state;
|
|
||||||
mod admin;
|
mod admin;
|
||||||
mod agent;
|
mod agent;
|
||||||
mod animation;
|
mod animation;
|
||||||
mod body;
|
mod body;
|
||||||
|
mod character_state;
|
||||||
mod controller;
|
mod controller;
|
||||||
mod inputs;
|
mod inputs;
|
||||||
mod inventory;
|
mod inventory;
|
||||||
@ -13,18 +13,16 @@ mod stats;
|
|||||||
mod visual;
|
mod visual;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use action_state::ActionState;
|
|
||||||
pub use admin::Admin;
|
pub use admin::Admin;
|
||||||
pub use agent::Agent;
|
pub use agent::Agent;
|
||||||
pub use animation::{Animation, AnimationInfo};
|
pub use animation::{Animation, AnimationInfo};
|
||||||
pub use body::{humanoid, object, quadruped, quadruped_medium, Body};
|
pub use body::{humanoid, object, quadruped, quadruped_medium, Body};
|
||||||
|
pub use character_state::{ActionState, CharacterState, MovementState};
|
||||||
pub use controller::Controller;
|
pub use controller::Controller;
|
||||||
pub use inputs::{
|
pub use inputs::CanBuild;
|
||||||
Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding,
|
|
||||||
};
|
|
||||||
pub use inventory::{item, Inventory, InventoryUpdate, Item};
|
pub use inventory::{item, Inventory, InventoryUpdate, Item};
|
||||||
pub use last::Last;
|
pub use last::Last;
|
||||||
pub use phys::{ForceUpdate, Ori, Pos, Scale, Vel};
|
pub use phys::{ForceUpdate, Ori, PhysicsState, Pos, Scale, Vel};
|
||||||
pub use player::Player;
|
pub use player::Player;
|
||||||
pub use stats::{Dying, Exp, HealthSource, Level, Stats};
|
pub use stats::{Exp, HealthSource, Level, Stats};
|
||||||
pub use visual::LightEmitter;
|
pub use visual::LightEmitter;
|
||||||
|
@ -34,6 +34,16 @@ impl Component for Scale {
|
|||||||
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PhysicsState
|
||||||
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct PhysicsState {
|
||||||
|
pub on_ground: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for PhysicsState {
|
||||||
|
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
// ForceUpdate
|
// ForceUpdate
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct ForceUpdate;
|
pub struct ForceUpdate;
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
|
use crate::comp;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use specs::Entity as EcsEntity;
|
use specs::Entity as EcsEntity;
|
||||||
use std::{collections::VecDeque, ops::DerefMut};
|
use std::{collections::VecDeque, ops::DerefMut};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
LandOnGround { entity: EcsEntity, vel: Vec3<f32> },
|
LandOnGround {
|
||||||
Explosion { pos: Vec3<f32>, radius: f32 },
|
entity: EcsEntity,
|
||||||
|
vel: Vec3<f32>,
|
||||||
|
},
|
||||||
|
Explosion {
|
||||||
|
pos: Vec3<f32>,
|
||||||
|
radius: f32,
|
||||||
|
},
|
||||||
|
Die {
|
||||||
|
entity: EcsEntity,
|
||||||
|
cause: comp::HealthSource,
|
||||||
|
},
|
||||||
|
Jump(EcsEntity),
|
||||||
|
Respawn(EcsEntity),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -51,9 +51,9 @@ pub enum ServerMsg {
|
|||||||
entity: u64,
|
entity: u64,
|
||||||
ori: comp::Ori,
|
ori: comp::Ori,
|
||||||
},
|
},
|
||||||
EntityActionState {
|
EntityCharacterState {
|
||||||
entity: u64,
|
entity: u64,
|
||||||
action_state: comp::ActionState,
|
character_state: comp::CharacterState,
|
||||||
},
|
},
|
||||||
InventoryUpdate(comp::Inventory),
|
InventoryUpdate(comp::Inventory),
|
||||||
TerrainChunkUpdate {
|
TerrainChunkUpdate {
|
||||||
|
@ -122,7 +122,8 @@ impl State {
|
|||||||
ecs.register::<comp::Controller>();
|
ecs.register::<comp::Controller>();
|
||||||
|
|
||||||
// Register components send directly from server -> all but one client
|
// Register components send directly from server -> all but one client
|
||||||
ecs.register::<comp::ActionState>();
|
ecs.register::<comp::CharacterState>();
|
||||||
|
ecs.register::<comp::PhysicsState>();
|
||||||
|
|
||||||
// Register components synced from client -> server -> all other clients
|
// Register components synced from client -> server -> all other clients
|
||||||
ecs.register::<comp::Pos>();
|
ecs.register::<comp::Pos>();
|
||||||
@ -132,27 +133,17 @@ impl State {
|
|||||||
|
|
||||||
// Register client-local components
|
// Register client-local components
|
||||||
ecs.register::<comp::AnimationInfo>();
|
ecs.register::<comp::AnimationInfo>();
|
||||||
ecs.register::<comp::Jumping>();
|
|
||||||
|
|
||||||
// Register server-local components
|
// Register server-local components
|
||||||
ecs.register::<comp::Last<comp::Pos>>();
|
ecs.register::<comp::Last<comp::Pos>>();
|
||||||
ecs.register::<comp::Last<comp::Vel>>();
|
ecs.register::<comp::Last<comp::Vel>>();
|
||||||
ecs.register::<comp::Last<comp::Ori>>();
|
ecs.register::<comp::Last<comp::Ori>>();
|
||||||
ecs.register::<comp::Last<comp::ActionState>>();
|
ecs.register::<comp::Last<comp::CharacterState>>();
|
||||||
ecs.register::<comp::Agent>();
|
ecs.register::<comp::Agent>();
|
||||||
ecs.register::<comp::Respawning>();
|
|
||||||
ecs.register::<comp::Dying>();
|
|
||||||
ecs.register::<comp::ForceUpdate>();
|
ecs.register::<comp::ForceUpdate>();
|
||||||
ecs.register::<comp::InventoryUpdate>();
|
ecs.register::<comp::InventoryUpdate>();
|
||||||
ecs.register::<comp::Inventory>();
|
ecs.register::<comp::Inventory>();
|
||||||
ecs.register::<comp::Admin>();
|
ecs.register::<comp::Admin>();
|
||||||
// Controller effects
|
|
||||||
ecs.register::<comp::MoveDir>();
|
|
||||||
ecs.register::<comp::OnGround>();
|
|
||||||
ecs.register::<comp::Attacking>();
|
|
||||||
ecs.register::<comp::Wielding>();
|
|
||||||
ecs.register::<comp::Rolling>();
|
|
||||||
ecs.register::<comp::Gliding>();
|
|
||||||
|
|
||||||
// Register synced resources used by the ECS.
|
// Register synced resources used by the ECS.
|
||||||
ecs.add_resource_synced(TimeOfDay(0.0));
|
ecs.add_resource_synced(TimeOfDay(0.0));
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
comp::{ActionState, Attacking, Controller, Gliding, OnGround, Rolling, Vel, Wielding},
|
|
||||||
sys::movement::MOVEMENT_THRESHOLD_VEL,
|
|
||||||
};
|
|
||||||
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
|
|
||||||
|
|
||||||
/// This system will set the ActionState component as specified by other components
|
|
||||||
pub struct Sys;
|
|
||||||
impl<'a> System<'a> for Sys {
|
|
||||||
type SystemData = (
|
|
||||||
Entities<'a>,
|
|
||||||
ReadStorage<'a, Controller>,
|
|
||||||
ReadStorage<'a, Vel>,
|
|
||||||
ReadStorage<'a, OnGround>,
|
|
||||||
ReadStorage<'a, Gliding>,
|
|
||||||
ReadStorage<'a, Attacking>,
|
|
||||||
ReadStorage<'a, Wielding>,
|
|
||||||
ReadStorage<'a, Rolling>,
|
|
||||||
WriteStorage<'a, ActionState>,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&mut self,
|
|
||||||
(
|
|
||||||
entities,
|
|
||||||
controllers, // To make sure it only runs on the single client and the server
|
|
||||||
velocities,
|
|
||||||
on_grounds,
|
|
||||||
glidings,
|
|
||||||
attackings,
|
|
||||||
wieldings,
|
|
||||||
rollings,
|
|
||||||
mut action_states,
|
|
||||||
): Self::SystemData,
|
|
||||||
) {
|
|
||||||
for (
|
|
||||||
_entity,
|
|
||||||
vel,
|
|
||||||
_controller,
|
|
||||||
on_ground,
|
|
||||||
gliding,
|
|
||||||
attacking,
|
|
||||||
wielding,
|
|
||||||
rolling,
|
|
||||||
action_state,
|
|
||||||
) in (
|
|
||||||
&entities,
|
|
||||||
&velocities,
|
|
||||||
&controllers,
|
|
||||||
on_grounds.maybe(),
|
|
||||||
glidings.maybe(),
|
|
||||||
attackings.maybe(),
|
|
||||||
wieldings.maybe(),
|
|
||||||
rollings.maybe(),
|
|
||||||
&mut action_states,
|
|
||||||
)
|
|
||||||
.join()
|
|
||||||
{
|
|
||||||
*action_state = ActionState {
|
|
||||||
on_ground: on_ground.is_some(),
|
|
||||||
moving: vel.0.magnitude_squared() > MOVEMENT_THRESHOLD_VEL.powf(2.0),
|
|
||||||
attacking: attacking.is_some(),
|
|
||||||
wielding: wielding.is_some(),
|
|
||||||
rolling: rolling.is_some(),
|
|
||||||
gliding: gliding.is_some(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
use crate::comp::{ActionState, Agent, Controller, Pos, Stats};
|
use crate::comp::{Agent, CharacterState, Controller, MovementState::Glide, Pos, Stats};
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -10,14 +10,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Stats>,
|
ReadStorage<'a, Stats>,
|
||||||
ReadStorage<'a, ActionState>,
|
ReadStorage<'a, CharacterState>,
|
||||||
WriteStorage<'a, Agent>,
|
WriteStorage<'a, Agent>,
|
||||||
WriteStorage<'a, Controller>,
|
WriteStorage<'a, Controller>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&mut self,
|
&mut self,
|
||||||
(entities, positions, stats, action_states, mut agents, mut controllers): Self::SystemData,
|
(entities, positions, stats, character_states, mut agents, mut controllers): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
for (entity, pos, agent, controller) in
|
for (entity, pos, agent, controller) in
|
||||||
(&entities, &positions, &mut agents, &mut controllers).join()
|
(&entities, &positions, &mut agents, &mut controllers).join()
|
||||||
@ -67,12 +67,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
const SIGHT_DIST: f32 = 30.0;
|
const SIGHT_DIST: f32 = 30.0;
|
||||||
let mut choose_new = false;
|
let mut choose_new = false;
|
||||||
|
|
||||||
if let Some((Some(target_pos), Some(target_stats), Some(a))) =
|
if let Some((Some(target_pos), Some(target_stats), Some(target_character))) =
|
||||||
target.map(|target| {
|
target.map(|target| {
|
||||||
(
|
(
|
||||||
positions.get(target),
|
positions.get(target),
|
||||||
stats.get(target),
|
stats.get(target),
|
||||||
action_states.get(target),
|
character_states.get(target),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -97,7 +97,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
controller.roll = true;
|
controller.roll = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.gliding && target_pos.0.z > pos.0.z + 5.0 {
|
if target_character.movement == Glide && target_pos.0.z > pos.0.z + 5.0
|
||||||
|
{
|
||||||
controller.glide = true;
|
controller.glide = true;
|
||||||
controller.jump = true;
|
controller.jump = true;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{ActionState, Animation, AnimationInfo},
|
comp::{
|
||||||
|
ActionState::*, Animation, AnimationInfo, CharacterState, MovementState::*, PhysicsState,
|
||||||
|
},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// This system will apply the animation that fits best to the users actions
|
/// This system will apply the animation that fits best to the users actions
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
@ -10,37 +13,32 @@ impl<'a> System<'a> for Sys {
|
|||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
ReadStorage<'a, ActionState>,
|
ReadStorage<'a, CharacterState>,
|
||||||
|
ReadStorage<'a, PhysicsState>,
|
||||||
WriteStorage<'a, AnimationInfo>,
|
WriteStorage<'a, AnimationInfo>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, (entities, dt, action_states, mut animation_infos): Self::SystemData) {
|
fn run(
|
||||||
for (entity, a) in (&entities, &action_states).join() {
|
&mut self,
|
||||||
fn impossible_animation(message: &str) -> Animation {
|
(entities, dt, character_states, physics_states, mut animation_infos): Self::SystemData,
|
||||||
warn!("{}", message);
|
) {
|
||||||
Animation::Idle
|
for (entity, character, physics) in (&entities, &character_states, &physics_states).join() {
|
||||||
|
fn impossible_animation(physics: PhysicsState, character: CharacterState) -> Animation {
|
||||||
|
warn!("Impossible animation: {:?} {:?}", physics, character);
|
||||||
|
Animation::Roll
|
||||||
}
|
}
|
||||||
let animation = match (
|
|
||||||
a.on_ground,
|
let animation = match (physics.on_ground, &character.movement, &character.action) {
|
||||||
a.moving,
|
(_, Roll { .. }, Idle) => Animation::Roll,
|
||||||
a.attacking,
|
(true, Stand, Idle) => Animation::Idle,
|
||||||
a.gliding,
|
(true, Run, Idle) => Animation::Run,
|
||||||
a.rolling,
|
(false, Jump, Idle) => Animation::Jump,
|
||||||
a.wielding,
|
(true, Stand, Wield { .. }) => Animation::Cidle,
|
||||||
) {
|
(true, Run, Wield { .. }) => Animation::Crun,
|
||||||
(_, _, true, true, _, _) => impossible_animation("Attack while gliding"),
|
(false, Jump, Wield { .. }) => Animation::Cjump,
|
||||||
(_, _, true, _, true, _) => impossible_animation("Roll while attacking"),
|
(_, Glide, Idle) => Animation::Gliding,
|
||||||
(_, _, _, true, true, _) => impossible_animation("Roll while gliding"),
|
(_, _, Attack { .. }) => Animation::Attack,
|
||||||
(_, false, _, _, true, _) => impossible_animation("Roll without moving"),
|
_ => impossible_animation(physics.clone(), character.clone()),
|
||||||
(_, true, false, false, true, _) => Animation::Roll,
|
|
||||||
(true, false, false, false, false, false) => Animation::Idle,
|
|
||||||
(true, true, false, false, false, false) => Animation::Run,
|
|
||||||
(false, _, false, false, false, false) => Animation::Jump,
|
|
||||||
(true, false, false, false, false, true) => Animation::Cidle,
|
|
||||||
(true, true, false, false, false, true) => Animation::Crun,
|
|
||||||
(false, _, false, false, false, true) => Animation::Cjump,
|
|
||||||
(_, _, false, true, false, _) => Animation::Gliding,
|
|
||||||
(_, _, true, false, false, _) => Animation::Attack,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_time = animation_infos
|
let new_time = animation_infos
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{Attacking, ForceUpdate, HealthSource, Ori, Pos, Stats, Vel, Wielding},
|
comp::{ActionState::*, CharacterState, ForceUpdate, HealthSource, Ori, Pos, Stats, Vel},
|
||||||
state::{DeltaTime, Uid},
|
state::{DeltaTime, Uid},
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
/// This system is responsible for handling accepted inputs like moving or attacking
|
/// This system is responsible for handling accepted inputs like moving or attacking
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
@ -14,8 +15,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Ori>,
|
ReadStorage<'a, Ori>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Attacking>,
|
WriteStorage<'a, CharacterState>,
|
||||||
WriteStorage<'a, Wielding>,
|
|
||||||
WriteStorage<'a, Stats>,
|
WriteStorage<'a, Stats>,
|
||||||
WriteStorage<'a, ForceUpdate>,
|
WriteStorage<'a, ForceUpdate>,
|
||||||
);
|
);
|
||||||
@ -29,18 +29,26 @@ impl<'a> System<'a> for Sys {
|
|||||||
positions,
|
positions,
|
||||||
orientations,
|
orientations,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut attackings,
|
mut character_states,
|
||||||
mut wieldings,
|
|
||||||
mut stats,
|
mut stats,
|
||||||
mut force_updates,
|
mut force_updates,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
// Attacks
|
// Attacks
|
||||||
(&entities, &uids, &positions, &orientations, &mut attackings)
|
for (entity, uid, pos, ori, mut character) in (
|
||||||
|
&entities,
|
||||||
|
&uids,
|
||||||
|
&positions,
|
||||||
|
&orientations,
|
||||||
|
&mut character_states,
|
||||||
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter_map(|(entity, uid, pos, ori, mut attacking)| {
|
{
|
||||||
if !attacking.applied {
|
let mut todo_end = false;
|
||||||
// Go through all other entities
|
|
||||||
|
// Go through all other entities
|
||||||
|
if let Attack { time_left, applied } = &mut character.action {
|
||||||
|
if !*applied {
|
||||||
for (b, pos_b, mut vel_b, stat_b) in
|
for (b, pos_b, mut vel_b, stat_b) in
|
||||||
(&entities, &positions, &mut velocities, &mut stats).join()
|
(&entities, &positions, &mut velocities, &mut stats).join()
|
||||||
{
|
{
|
||||||
@ -59,29 +67,28 @@ impl<'a> System<'a> for Sys {
|
|||||||
let _ = force_updates.insert(b, ForceUpdate);
|
let _ = force_updates.insert(b, ForceUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attacking.applied = true;
|
*applied = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if attacking.time > 0.5 {
|
if *time_left == Duration::default() {
|
||||||
Some(entity)
|
todo_end = true;
|
||||||
} else {
|
} else {
|
||||||
attacking.time += dt.0;
|
*time_left = time_left
|
||||||
|
.checked_sub(Duration::from_secs_f32(dt.0))
|
||||||
None
|
.unwrap_or_default();
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.collect::<Vec<_>>()
|
if todo_end {
|
||||||
.into_iter()
|
character.action = Wield {
|
||||||
.for_each(|e| {
|
time_left: Duration::default(),
|
||||||
attackings.remove(e);
|
};
|
||||||
});
|
}
|
||||||
{
|
|
||||||
// Wields
|
if let Wield { time_left } = &mut character.action {
|
||||||
for wielding in (&mut wieldings).join() {
|
if *time_left != Duration::default() {
|
||||||
if !wielding.applied && wielding.time > 0.3 {
|
*time_left = time_left
|
||||||
wielding.applied = true;
|
.checked_sub(Duration::from_secs_f32(dt.0))
|
||||||
} else {
|
.unwrap_or_default();
|
||||||
wielding.time += dt.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,125 +1,135 @@
|
|||||||
use crate::comp::{
|
use crate::{
|
||||||
ActionState, Attacking, Body, Controller, Gliding, Jumping, MoveDir, Respawning, Rolling,
|
comp::{
|
||||||
Stats, Vel, Wielding,
|
ActionState::*, Body, CharacterState, Controller, MovementState::*, PhysicsState, Stats,
|
||||||
|
Vel,
|
||||||
|
},
|
||||||
|
event::{Event, EventBus},
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
/// This system is responsible for validating controller inputs
|
/// This system is responsible for validating controller inputs
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
|
Read<'a, EventBus>,
|
||||||
WriteStorage<'a, Controller>,
|
WriteStorage<'a, Controller>,
|
||||||
ReadStorage<'a, Stats>,
|
ReadStorage<'a, Stats>,
|
||||||
ReadStorage<'a, Body>,
|
ReadStorage<'a, Body>,
|
||||||
ReadStorage<'a, Vel>,
|
ReadStorage<'a, Vel>,
|
||||||
WriteStorage<'a, ActionState>,
|
ReadStorage<'a, PhysicsState>,
|
||||||
WriteStorage<'a, MoveDir>,
|
WriteStorage<'a, CharacterState>,
|
||||||
WriteStorage<'a, Jumping>,
|
|
||||||
WriteStorage<'a, Attacking>,
|
|
||||||
WriteStorage<'a, Wielding>,
|
|
||||||
WriteStorage<'a, Rolling>,
|
|
||||||
WriteStorage<'a, Respawning>,
|
|
||||||
WriteStorage<'a, Gliding>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&mut self,
|
&mut self,
|
||||||
(
|
(
|
||||||
entities,
|
entities,
|
||||||
|
event_bus,
|
||||||
mut controllers,
|
mut controllers,
|
||||||
stats,
|
stats,
|
||||||
bodies,
|
bodies,
|
||||||
velocities,
|
velocities,
|
||||||
mut action_states,
|
physics_states,
|
||||||
mut move_dirs,
|
mut character_states,
|
||||||
mut jumpings,
|
|
||||||
mut attackings,
|
|
||||||
mut wieldings,
|
|
||||||
mut rollings,
|
|
||||||
mut respawns,
|
|
||||||
mut glidings,
|
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
for (entity, controller, stats, body, vel, mut a) in (
|
let mut event_emitter = event_bus.emitter();
|
||||||
|
|
||||||
|
for (entity, controller, stats, body, vel, physics, mut character) in (
|
||||||
&entities,
|
&entities,
|
||||||
&mut controllers,
|
&mut controllers,
|
||||||
&stats,
|
&stats,
|
||||||
&bodies,
|
&bodies,
|
||||||
&velocities,
|
&velocities,
|
||||||
// Although this is changed, it is only kept for this system
|
&physics_states,
|
||||||
// as it will be replaced in the action state system
|
&mut character_states,
|
||||||
&mut action_states,
|
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
if stats.is_dead {
|
if stats.is_dead {
|
||||||
// Respawn
|
// Respawn
|
||||||
if controller.respawn {
|
if controller.respawn {
|
||||||
let _ = respawns.insert(entity, Respawning);
|
event_emitter.emit(Event::Respawn(entity));
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move dir
|
// Move
|
||||||
if !a.rolling {
|
controller.move_dir = if controller.move_dir.magnitude_squared() > 1.0 {
|
||||||
let _ = move_dirs.insert(
|
controller.move_dir.normalized()
|
||||||
entity,
|
} else {
|
||||||
MoveDir(if controller.move_dir.magnitude_squared() > 1.0 {
|
controller.move_dir
|
||||||
controller.move_dir.normalized()
|
};
|
||||||
} else {
|
|
||||||
controller.move_dir
|
if character.movement == Stand && controller.move_dir.magnitude_squared() > 0.0 {
|
||||||
}),
|
character.movement = Run;
|
||||||
);
|
} else if character.movement == Run && controller.move_dir.magnitude_squared() == 0.0 {
|
||||||
|
character.movement = Stand;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Glide
|
// Glide
|
||||||
if controller.glide && !a.on_ground && !a.attacking && !a.rolling && body.is_humanoid()
|
if controller.glide
|
||||||
|
&& !physics.on_ground
|
||||||
|
&& (character.action == Idle || character.action.is_wield())
|
||||||
|
&& character.movement == Jump
|
||||||
|
// TODO: Ask zesterer if we can remove this
|
||||||
|
&& body.is_humanoid()
|
||||||
{
|
{
|
||||||
let _ = glidings.insert(entity, Gliding);
|
character.movement = Glide;
|
||||||
a.gliding = true;
|
} else if !controller.glide && character.movement == Glide {
|
||||||
} else {
|
character.movement = Jump;
|
||||||
let _ = glidings.remove(entity);
|
|
||||||
a.gliding = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wield
|
// Wield
|
||||||
if controller.attack && !a.wielding && !a.gliding && !a.rolling {
|
if controller.attack
|
||||||
let _ = wieldings.insert(entity, Wielding::start());
|
&& character.action == Idle
|
||||||
a.wielding = true;
|
&& (character.movement == Stand || character.movement == Run)
|
||||||
|
{
|
||||||
|
character.action = Wield {
|
||||||
|
time_left: Duration::from_millis(300),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attack
|
// Attack
|
||||||
if controller.attack
|
if controller.attack
|
||||||
&& !a.attacking
|
&& (character.movement == Stand
|
||||||
&& wieldings.get(entity).map(|w| w.applied).unwrap_or(false)
|
|| character.movement == Run
|
||||||
&& !a.gliding
|
|| character.movement == Jump)
|
||||||
&& !a.rolling
|
|
||||||
{
|
{
|
||||||
let _ = attackings.insert(entity, Attacking::start());
|
// TODO: Check if wield ability exists
|
||||||
a.attacking = true;
|
if let Wield { time_left } = character.action {
|
||||||
|
if time_left == Duration::default() {
|
||||||
|
character.action = Attack {
|
||||||
|
time_left: Duration::from_millis(300),
|
||||||
|
applied: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Roll
|
// Roll
|
||||||
if controller.roll
|
if controller.roll
|
||||||
&& !a.rolling
|
&& (character.action == Idle || character.action.is_wield())
|
||||||
&& a.on_ground
|
&& character.movement == Run
|
||||||
&& a.moving
|
&& physics.on_ground
|
||||||
&& !a.attacking
|
|
||||||
&& !a.gliding
|
|
||||||
{
|
{
|
||||||
let _ = rollings.insert(entity, Rolling::start());
|
character.movement = Roll {
|
||||||
a.rolling = true;
|
time_left: Duration::from_millis(600),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump
|
// Jump
|
||||||
if controller.jump && a.on_ground && vel.0.z <= 0.0 {
|
if controller.jump && physics.on_ground && vel.0.z <= 0.0 {
|
||||||
let _ = jumpings.insert(entity, Jumping);
|
dbg!();
|
||||||
a.on_ground = false;
|
event_emitter.emit(Event::Jump(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO before merge: reset controller in a final ecs system
|
||||||
|
|
||||||
// Reset the controller ready for the next tick
|
// Reset the controller ready for the next tick
|
||||||
*controller = Controller::default();
|
//*controller = Controller::default();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
mod action_state;
|
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
pub mod animation;
|
pub mod animation;
|
||||||
pub mod combat;
|
pub mod combat;
|
||||||
@ -13,7 +12,6 @@ use specs::DispatcherBuilder;
|
|||||||
// System names
|
// System names
|
||||||
const AGENT_SYS: &str = "agent_sys";
|
const AGENT_SYS: &str = "agent_sys";
|
||||||
const CONTROLLER_SYS: &str = "controller_sys";
|
const CONTROLLER_SYS: &str = "controller_sys";
|
||||||
const ACTION_STATE_SYS: &str = "action_state_sys";
|
|
||||||
const PHYS_SYS: &str = "phys_sys";
|
const PHYS_SYS: &str = "phys_sys";
|
||||||
const MOVEMENT_SYS: &str = "movement_sys";
|
const MOVEMENT_SYS: &str = "movement_sys";
|
||||||
const COMBAT_SYS: &str = "combat_sys";
|
const COMBAT_SYS: &str = "combat_sys";
|
||||||
@ -25,12 +23,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
|||||||
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]);
|
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]);
|
||||||
dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS]);
|
dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS]);
|
||||||
dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[PHYS_SYS]);
|
dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[PHYS_SYS]);
|
||||||
dispatch_builder.add(
|
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[CONTROLLER_SYS]);
|
||||||
action_state::Sys,
|
dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[CONTROLLER_SYS]);
|
||||||
ACTION_STATE_SYS,
|
|
||||||
&[CONTROLLER_SYS, PHYS_SYS],
|
|
||||||
);
|
|
||||||
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[ACTION_STATE_SYS]);
|
|
||||||
dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[ACTION_STATE_SYS]);
|
|
||||||
dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]);
|
dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{ActionState, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Stats, Vel, Wielding},
|
comp::{
|
||||||
|
ActionState::*, CharacterState, Controller, MovementState::*, Ori, PhysicsState, Pos,
|
||||||
|
Stats, Vel,
|
||||||
|
},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
terrain::TerrainMap,
|
terrain::TerrainMap,
|
||||||
vol::{ReadVol, Vox},
|
vol::{ReadVol, Vox},
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
||||||
|
use std::time::Duration;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
const HUMANOID_ACCEL: f32 = 70.0;
|
const HUMANOID_ACCEL: f32 = 70.0;
|
||||||
@ -13,7 +17,6 @@ const WIELD_ACCEL: f32 = 70.0;
|
|||||||
const WIELD_SPEED: f32 = 120.0;
|
const WIELD_SPEED: f32 = 120.0;
|
||||||
const HUMANOID_AIR_ACCEL: f32 = 10.0;
|
const HUMANOID_AIR_ACCEL: f32 = 10.0;
|
||||||
const HUMANOID_AIR_SPEED: f32 = 100.0;
|
const HUMANOID_AIR_SPEED: f32 = 100.0;
|
||||||
const HUMANOID_JUMP_ACCEL: f32 = 18.0;
|
|
||||||
const ROLL_ACCEL: f32 = 160.0;
|
const ROLL_ACCEL: f32 = 160.0;
|
||||||
const ROLL_SPEED: f32 = 550.0;
|
const ROLL_SPEED: f32 = 550.0;
|
||||||
const GLIDE_ACCEL: f32 = 15.0;
|
const GLIDE_ACCEL: f32 = 15.0;
|
||||||
@ -30,13 +33,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
ReadExpect<'a, TerrainMap>,
|
ReadExpect<'a, TerrainMap>,
|
||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
ReadStorage<'a, MoveDir>,
|
|
||||||
ReadStorage<'a, Stats>,
|
ReadStorage<'a, Stats>,
|
||||||
ReadStorage<'a, ActionState>,
|
ReadStorage<'a, Controller>,
|
||||||
WriteStorage<'a, Jumping>,
|
ReadStorage<'a, PhysicsState>,
|
||||||
WriteStorage<'a, Wielding>,
|
WriteStorage<'a, CharacterState>,
|
||||||
WriteStorage<'a, Rolling>,
|
|
||||||
WriteStorage<'a, OnGround>,
|
|
||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Ori>,
|
WriteStorage<'a, Ori>,
|
||||||
@ -48,105 +48,87 @@ impl<'a> System<'a> for Sys {
|
|||||||
entities,
|
entities,
|
||||||
terrain,
|
terrain,
|
||||||
dt,
|
dt,
|
||||||
move_dirs,
|
|
||||||
stats,
|
stats,
|
||||||
action_states,
|
controllers,
|
||||||
mut jumpings,
|
physics_states,
|
||||||
mut wieldings,
|
mut character_states,
|
||||||
mut rollings,
|
|
||||||
mut on_grounds,
|
|
||||||
mut positions,
|
mut positions,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut orientations,
|
mut orientations,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
// Apply movement inputs
|
// Apply movement inputs
|
||||||
for (entity, stats, a, move_dir, mut pos, mut vel, mut ori) in (
|
for (entity, stats, controller, physics, mut character, mut pos, mut vel, mut ori) in (
|
||||||
&entities,
|
&entities,
|
||||||
&stats,
|
&stats,
|
||||||
&action_states,
|
&controllers,
|
||||||
move_dirs.maybe(),
|
&physics_states,
|
||||||
|
&mut character_states,
|
||||||
&mut positions,
|
&mut positions,
|
||||||
&mut velocities,
|
&mut velocities,
|
||||||
&mut orientations,
|
&mut orientations,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
// Disable while dead TODO: Replace with client states?
|
|
||||||
if stats.is_dead {
|
if stats.is_dead {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move player according to move_dir
|
// Move player according to move_dir
|
||||||
if let Some(move_dir) = move_dir {
|
vel.0 += Vec2::broadcast(dt.0)
|
||||||
vel.0 += Vec2::broadcast(dt.0)
|
* controller.move_dir
|
||||||
* move_dir.0
|
* match (physics.on_ground, &character.movement) {
|
||||||
* match (a.on_ground, a.gliding, a.rolling, a.wielding) {
|
(true, Run) if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => {
|
||||||
(true, false, false, false)
|
HUMANOID_ACCEL
|
||||||
if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) =>
|
}
|
||||||
{
|
(false, Glide) if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) => {
|
||||||
HUMANOID_ACCEL
|
GLIDE_ACCEL
|
||||||
}
|
}
|
||||||
(false, true, false, false)
|
(false, Jump) if vel.0.magnitude_squared() < HUMANOID_AIR_SPEED.powf(2.0) => {
|
||||||
if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) =>
|
HUMANOID_AIR_ACCEL
|
||||||
{
|
}
|
||||||
GLIDE_ACCEL
|
(true, Roll { .. }) if vel.0.magnitude_squared() < ROLL_SPEED.powf(2.0) => {
|
||||||
}
|
ROLL_ACCEL
|
||||||
(false, false, false, false)
|
}
|
||||||
if vel.0.magnitude_squared() < HUMANOID_AIR_SPEED.powf(2.0) =>
|
_ => 0.0,
|
||||||
{
|
|
||||||
HUMANOID_AIR_ACCEL
|
|
||||||
}
|
|
||||||
(true, false, true, _)
|
|
||||||
if vel.0.magnitude_squared() < ROLL_SPEED.powf(2.0) =>
|
|
||||||
{
|
|
||||||
ROLL_ACCEL
|
|
||||||
}
|
|
||||||
(true, false, false, true)
|
|
||||||
if vel.0.magnitude_squared() < WIELD_SPEED.powf(2.0) =>
|
|
||||||
{
|
|
||||||
WIELD_ACCEL
|
|
||||||
}
|
|
||||||
_ => 0.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set direction based on move direction when on the ground
|
|
||||||
let ori_dir = if a.gliding || a.rolling {
|
|
||||||
Vec2::from(vel.0)
|
|
||||||
} else {
|
|
||||||
move_dir.0
|
|
||||||
};
|
};
|
||||||
if ori_dir.magnitude_squared() > 0.0001
|
|
||||||
&& (ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared()
|
|
||||||
> 0.001
|
|
||||||
{
|
|
||||||
ori.0 = vek::ops::Slerp::slerp(
|
|
||||||
ori.0,
|
|
||||||
ori_dir.into(),
|
|
||||||
if a.on_ground { 12.0 } else { 2.0 } * dt.0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump
|
// Set direction based on move direction when on the ground
|
||||||
if jumpings.get(entity).is_some() {
|
let ori_dir = if character.movement == Glide || character.movement.is_roll() {
|
||||||
vel.0.z = HUMANOID_JUMP_ACCEL;
|
Vec2::from(vel.0)
|
||||||
jumpings.remove(entity);
|
} else {
|
||||||
|
controller.move_dir
|
||||||
|
};
|
||||||
|
if ori_dir.magnitude_squared() > 0.0001
|
||||||
|
&& (ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared()
|
||||||
|
> 0.001
|
||||||
|
{
|
||||||
|
ori.0 = vek::ops::Slerp::slerp(
|
||||||
|
ori.0,
|
||||||
|
ori_dir.into(),
|
||||||
|
if physics.on_ground { 12.0 } else { 2.0 } * dt.0,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Glide
|
// Glide
|
||||||
if a.gliding && vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) && vel.0.z < 0.0 {
|
if character.movement == Glide
|
||||||
let _ = wieldings.remove(entity);
|
&& vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0)
|
||||||
|
&& vel.0.z < 0.0
|
||||||
|
{
|
||||||
|
character.action = Idle;
|
||||||
let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
|
let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
|
||||||
vel.0.z += dt.0 * lift * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
|
vel.0.z += dt.0 * lift * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Roll
|
// Roll
|
||||||
if let Some(time) = rollings.get_mut(entity).map(|r| &mut r.time) {
|
if let Roll { time_left } = &mut character.movement {
|
||||||
let _ = wieldings.remove(entity);
|
character.action = Idle;
|
||||||
*time += dt.0;
|
if *time_left == Duration::default() || vel.0.magnitude_squared() < 10.0 {
|
||||||
if *time > 0.6 || !a.moving {
|
character.movement = Run;
|
||||||
rollings.remove(entity);
|
} else {
|
||||||
|
*time_left = time_left
|
||||||
|
.checked_sub(Duration::from_secs_f32(dt.0))
|
||||||
|
.unwrap_or_default();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
use crate::{
|
use {
|
||||||
comp::HealthSource,
|
crate::{
|
||||||
comp::{
|
comp::{Body, CharacterState, MovementState::*, Ori, PhysicsState, Pos, Scale, Stats, Vel},
|
||||||
ActionState, Body, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Scale, Stats, Vel,
|
event::{Event, EventBus},
|
||||||
Wielding,
|
state::DeltaTime,
|
||||||
|
terrain::TerrainMap,
|
||||||
|
vol::{ReadVol, Vox},
|
||||||
},
|
},
|
||||||
event::{Event, EventBus},
|
specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage},
|
||||||
state::DeltaTime,
|
vek::*,
|
||||||
terrain::TerrainMap,
|
|
||||||
vol::{ReadVol, Vox},
|
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
|
||||||
use vek::*;
|
|
||||||
|
|
||||||
const GRAVITY: f32 = 9.81 * 4.0;
|
const GRAVITY: f32 = 9.81 * 4.0;
|
||||||
const FRIC_GROUND: f32 = 0.15;
|
const FRIC_GROUND: f32 = 0.15;
|
||||||
@ -37,10 +35,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadExpect<'a, TerrainMap>,
|
ReadExpect<'a, TerrainMap>,
|
||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
Read<'a, EventBus>,
|
Read<'a, EventBus>,
|
||||||
ReadStorage<'a, ActionState>,
|
|
||||||
ReadStorage<'a, Scale>,
|
ReadStorage<'a, Scale>,
|
||||||
ReadStorage<'a, Body>,
|
ReadStorage<'a, Body>,
|
||||||
WriteStorage<'a, OnGround>,
|
WriteStorage<'a, CharacterState>,
|
||||||
|
WriteStorage<'a, PhysicsState>,
|
||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Ori>,
|
WriteStorage<'a, Ori>,
|
||||||
@ -53,10 +51,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
terrain,
|
terrain,
|
||||||
dt,
|
dt,
|
||||||
event_bus,
|
event_bus,
|
||||||
action_states,
|
|
||||||
scales,
|
scales,
|
||||||
bodies,
|
bodies,
|
||||||
mut on_grounds,
|
mut character_states,
|
||||||
|
mut physics_states,
|
||||||
mut positions,
|
mut positions,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut orientations,
|
mut orientations,
|
||||||
@ -65,9 +63,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut event_emitter = event_bus.emitter();
|
let mut event_emitter = event_bus.emitter();
|
||||||
|
|
||||||
// Apply movement inputs
|
// Apply movement inputs
|
||||||
for (entity, a, scale, b, mut pos, mut vel, mut ori) in (
|
for (entity, scale, b, mut pos, mut vel, mut ori) in (
|
||||||
&entities,
|
&entities,
|
||||||
&action_states,
|
|
||||||
scales.maybe(),
|
scales.maybe(),
|
||||||
&bodies,
|
&bodies,
|
||||||
&mut positions,
|
&mut positions,
|
||||||
@ -76,12 +73,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
|
let mut character_state = character_states.get(entity).cloned().unwrap_or_default();
|
||||||
|
let mut physics_state = physics_states.get(entity).cloned().unwrap_or_default();
|
||||||
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
let scale = scale.map(|s| s.0).unwrap_or(1.0);
|
||||||
|
|
||||||
// Integrate forces
|
// Integrate forces
|
||||||
// Friction is assumed to be a constant dependent on location
|
// Friction is assumed to be a constant dependent on location
|
||||||
let friction = 50.0
|
let friction = 50.0
|
||||||
* if on_grounds.get(entity).is_some() {
|
* if physics_state.on_ground {
|
||||||
FRIC_GROUND
|
FRIC_GROUND
|
||||||
} else {
|
} else {
|
||||||
FRIC_AIR
|
FRIC_AIR
|
||||||
@ -108,7 +107,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
if terrain
|
if terrain
|
||||||
.get(block_pos)
|
.get(block_pos)
|
||||||
.map(|vox| vox.is_solid())
|
.map(|vox| !vox.is_empty())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
let player_aabb = Aabb {
|
let player_aabb = Aabb {
|
||||||
@ -128,8 +127,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
let was_on_ground = a.on_ground;
|
let was_on_ground = physics_state.on_ground;
|
||||||
on_grounds.remove(entity); // Assume we're in the air - unless we can prove otherwise
|
physics_state.on_ground = false;
|
||||||
|
|
||||||
let mut on_ground = false;
|
let mut on_ground = false;
|
||||||
let mut attempts = 0; // Don't loop infinitely here
|
let mut attempts = 0; // Don't loop infinitely here
|
||||||
@ -183,7 +182,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.filter(|(block_pos, _)| {
|
.filter(|(block_pos, _)| {
|
||||||
terrain
|
terrain
|
||||||
.get(*block_pos)
|
.get(*block_pos)
|
||||||
.map(|vox| vox.is_solid())
|
.map(|vox| !vox.is_empty())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
})
|
})
|
||||||
// Find the maximum of the minimum collision axes (this bit is weird, trust me that it works)
|
// Find the maximum of the minimum collision axes (this bit is weird, trust me that it works)
|
||||||
@ -216,6 +215,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
if !was_on_ground {
|
if !was_on_ground {
|
||||||
event_emitter.emit(Event::LandOnGround { entity, vel: vel.0 });
|
event_emitter.emit(Event::LandOnGround { entity, vel: vel.0 });
|
||||||
|
character_state.movement = Stand;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,13 +257,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
if attempts == MAX_ATTEMPTS {
|
if attempts == MAX_ATTEMPTS {
|
||||||
pos.0 = old_pos;
|
pos.0 = old_pos;
|
||||||
vel.0 = Vec3::zero();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if on_ground {
|
if on_ground {
|
||||||
let _ = on_grounds.insert(entity, OnGround);
|
physics_state.on_ground = true;
|
||||||
|
|
||||||
|
if !was_on_ground {
|
||||||
|
character_state.movement = Stand;
|
||||||
|
}
|
||||||
// If the space below us is free, then "snap" to the ground
|
// If the space below us is free, then "snap" to the ground
|
||||||
} else if collision_with(pos.0 - Vec3::unit_z() * 1.05, near_iter.clone())
|
} else if collision_with(pos.0 - Vec3::unit_z() * 1.05, near_iter.clone())
|
||||||
&& vel.0.z < 0.0
|
&& vel.0.z < 0.0
|
||||||
@ -271,8 +274,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
&& was_on_ground
|
&& was_on_ground
|
||||||
{
|
{
|
||||||
pos.0.z = (pos.0.z - 0.05).floor();
|
pos.0.z = (pos.0.z - 0.05).floor();
|
||||||
let _ = on_grounds.insert(entity, OnGround);
|
physics_state.on_ground = true;
|
||||||
|
} else if was_on_ground {
|
||||||
|
character_state.movement = Jump;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = character_states.insert(entity, character_state);
|
||||||
|
let _ = physics_states.insert(entity, physics_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply pushback
|
// Apply pushback
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{Dying, HealthSource, Stats},
|
comp::{HealthSource, Stats},
|
||||||
|
event::{Event, EventBus},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
};
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
@ -11,27 +12,29 @@ impl<'a> System<'a> for Sys {
|
|||||||
type SystemData = (
|
type SystemData = (
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
|
Read<'a, EventBus>,
|
||||||
WriteStorage<'a, Stats>,
|
WriteStorage<'a, Stats>,
|
||||||
WriteStorage<'a, Dying>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, (entities, dt, mut stats, mut dyings): Self::SystemData) {
|
fn run(&mut self, (entities, dt, event_bus, mut stats): Self::SystemData) {
|
||||||
|
let mut event_emitter = event_bus.emitter();
|
||||||
|
|
||||||
for (entity, mut stat) in (&entities, &mut stats).join() {
|
for (entity, mut stat) in (&entities, &mut stats).join() {
|
||||||
if stat.should_die() && !stat.is_dead {
|
if stat.should_die() && !stat.is_dead {
|
||||||
let _ = dyings.insert(
|
event_emitter.emit(Event::Die {
|
||||||
entity,
|
entity,
|
||||||
Dying {
|
cause: match stat.health.last_change {
|
||||||
cause: match stat.health.last_change {
|
Some(change) => change.2,
|
||||||
Some(change) => change.2,
|
None => {
|
||||||
None => {
|
warn!("Nothing caused an entity to die!");
|
||||||
warn!("Nothing caused an entity to die!");
|
HealthSource::Unknown
|
||||||
HealthSource::Unknown
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
|
|
||||||
stat.is_dead = true;
|
stat.is_dead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(change) = &mut stat.health.last_change {
|
if let Some(change) = &mut stat.health.last_change {
|
||||||
change.1 += f64::from(dt.0);
|
change.1 += f64::from(dt.0);
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ use vek::*;
|
|||||||
use world::{ChunkSupplement, World};
|
use world::{ChunkSupplement, World};
|
||||||
|
|
||||||
const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
|
const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
|
||||||
|
const HUMANOID_JUMP_ACCEL: f32 = 18.0;
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
ClientConnected {
|
ClientConnected {
|
||||||
@ -153,8 +154,7 @@ impl Server {
|
|||||||
.with(comp::Controller::default())
|
.with(comp::Controller::default())
|
||||||
.with(body)
|
.with(body)
|
||||||
.with(comp::Stats::new(name))
|
.with(comp::Stats::new(name))
|
||||||
.with(comp::ActionState::default())
|
.with(comp::CharacterState::default())
|
||||||
.with(comp::ForceUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a static object entity
|
/// Build a static object entity
|
||||||
@ -175,8 +175,7 @@ impl Server {
|
|||||||
..comp::LightEmitter::default()
|
..comp::LightEmitter::default()
|
||||||
})
|
})
|
||||||
//.with(comp::LightEmitter::default())
|
//.with(comp::LightEmitter::default())
|
||||||
.with(comp::ActionState::default())
|
.with(comp::CharacterState::default())
|
||||||
.with(comp::ForceUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_player_character(
|
pub fn create_player_character(
|
||||||
@ -195,7 +194,7 @@ impl Server {
|
|||||||
state.write_component(entity, comp::Pos(spawn_point));
|
state.write_component(entity, comp::Pos(spawn_point));
|
||||||
state.write_component(entity, comp::Vel(Vec3::zero()));
|
state.write_component(entity, comp::Vel(Vec3::zero()));
|
||||||
state.write_component(entity, comp::Ori(Vec3::unit_y()));
|
state.write_component(entity, comp::Ori(Vec3::unit_y()));
|
||||||
state.write_component(entity, comp::ActionState::default());
|
state.write_component(entity, comp::CharacterState::default());
|
||||||
state.write_component(entity, comp::Inventory::default());
|
state.write_component(entity, comp::Inventory::default());
|
||||||
state.write_component(entity, comp::InventoryUpdate);
|
state.write_component(entity, comp::InventoryUpdate);
|
||||||
// Make sure physics are accepted.
|
// Make sure physics are accepted.
|
||||||
@ -221,6 +220,9 @@ impl Server {
|
|||||||
let terrain = self.state.ecs().read_resource::<TerrainMap>();
|
let terrain = self.state.ecs().read_resource::<TerrainMap>();
|
||||||
let mut block_change = self.state.ecs().write_resource::<BlockChange>();
|
let mut block_change = self.state.ecs().write_resource::<BlockChange>();
|
||||||
let mut stats = self.state.ecs().write_storage::<comp::Stats>();
|
let mut stats = self.state.ecs().write_storage::<comp::Stats>();
|
||||||
|
let mut positions = self.state.ecs().write_storage::<comp::Pos>();
|
||||||
|
let mut velocities = self.state.ecs().write_storage::<comp::Vel>();
|
||||||
|
let mut force_updates = self.state.ecs().write_storage::<comp::ForceUpdate>();
|
||||||
|
|
||||||
for event in self.state.ecs().read_resource::<EventBus>().recv_all() {
|
for event in self.state.ecs().read_resource::<EventBus>().recv_all() {
|
||||||
match event {
|
match event {
|
||||||
@ -250,6 +252,74 @@ impl Server {
|
|||||||
.cast();
|
.cast();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GameEvent::Jump(entity) => {
|
||||||
|
if let Some(vel) = velocities.get_mut(entity) {
|
||||||
|
vel.0.z = HUMANOID_JUMP_ACCEL;
|
||||||
|
let _ = force_updates.insert(entity, comp::ForceUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameEvent::Die { entity, cause } => {
|
||||||
|
// Chat message
|
||||||
|
if let Some(player) =
|
||||||
|
self.state.ecs().read_storage::<comp::Player>().get(entity)
|
||||||
|
{
|
||||||
|
let msg = if let comp::HealthSource::Attack { by } = cause {
|
||||||
|
self.state()
|
||||||
|
.ecs()
|
||||||
|
.entity_from_uid(by.into())
|
||||||
|
.and_then(|attacker| {
|
||||||
|
self.state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<comp::Player>()
|
||||||
|
.get(attacker)
|
||||||
|
.map(|attacker_alias| {
|
||||||
|
format!(
|
||||||
|
"{} was killed by {}",
|
||||||
|
&player.alias, &attacker_alias.alias
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.unwrap_or(format!("{} died", &player.alias));
|
||||||
|
|
||||||
|
self.clients.notify_registered(ServerMsg::kill(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give EXP to the client
|
||||||
|
if let Some(entity_stats) = stats.get(entity).cloned() {
|
||||||
|
if let comp::HealthSource::Attack { by } = cause {
|
||||||
|
self.state.ecs().entity_from_uid(by.into()).map(|attacker| {
|
||||||
|
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
||||||
|
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
||||||
|
attacker_stats.exp.change_by(
|
||||||
|
entity_stats.health.maximum() as f64 / 10.0
|
||||||
|
+ entity_stats.level.level() as f64 * 10.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(client) = self.clients.get_mut(&entity) {
|
||||||
|
let _ = velocities.insert(entity, comp::Vel(Vec3::zero()));
|
||||||
|
let _ = force_updates.insert(entity, comp::ForceUpdate);
|
||||||
|
client.force_state(ClientState::Dead);
|
||||||
|
} else {
|
||||||
|
//let _ = self.state.ecs_mut().delete_entity_synced(entity);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameEvent::Respawn(entity) => {
|
||||||
|
// Only clients can respawn
|
||||||
|
if let Some(client) = self.clients.get_mut(&entity) {
|
||||||
|
client.allow_state(ClientState::Character);
|
||||||
|
stats.get_mut(entity).map(|stats| stats.revive());
|
||||||
|
positions.get_mut(entity).map(|pos| pos.0.z += 20.0);
|
||||||
|
force_updates.insert(entity, comp::ForceUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,7 +427,7 @@ impl Server {
|
|||||||
.with(comp::Controller::default())
|
.with(comp::Controller::default())
|
||||||
.with(body)
|
.with(body)
|
||||||
.with(stats)
|
.with(stats)
|
||||||
.with(comp::ActionState::default())
|
.with(comp::CharacterState::default())
|
||||||
.with(comp::Agent::enemy())
|
.with(comp::Agent::enemy())
|
||||||
.with(comp::Scale(scale))
|
.with(comp::Scale(scale))
|
||||||
.build();
|
.build();
|
||||||
@ -475,13 +545,6 @@ impl Server {
|
|||||||
|
|
||||||
// 7) Finish the tick, pass control back to the frontend.
|
// 7) Finish the tick, pass control back to the frontend.
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
let ecs = self.state.ecs_mut();
|
|
||||||
for entity in ecs.entities().join() {
|
|
||||||
ecs.write_storage::<comp::Dying>().remove(entity);
|
|
||||||
ecs.write_storage::<comp::Respawning>().remove(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -885,12 +948,12 @@ impl Server {
|
|||||||
state.write_component(entity, player);
|
state.write_component(entity, player);
|
||||||
|
|
||||||
// Sync physics of all entities
|
// Sync physics of all entities
|
||||||
for (&uid, &pos, vel, ori, action_state) in (
|
for (&uid, &pos, vel, ori, character_state) in (
|
||||||
&state.ecs().read_storage::<Uid>(),
|
&state.ecs().read_storage::<Uid>(),
|
||||||
&state.ecs().read_storage::<comp::Pos>(), // We assume all these entities have a position
|
&state.ecs().read_storage::<comp::Pos>(), // We assume all these entities have a position
|
||||||
state.ecs().read_storage::<comp::Vel>().maybe(),
|
state.ecs().read_storage::<comp::Vel>().maybe(),
|
||||||
state.ecs().read_storage::<comp::Ori>().maybe(),
|
state.ecs().read_storage::<comp::Ori>().maybe(),
|
||||||
state.ecs().read_storage::<comp::ActionState>().maybe(),
|
state.ecs().read_storage::<comp::CharacterState>().maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
@ -910,10 +973,10 @@ impl Server {
|
|||||||
ori,
|
ori,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if let Some(action_state) = action_state.copied() {
|
if let Some(character_state) = character_state.copied() {
|
||||||
client.notify(ServerMsg::EntityActionState {
|
client.notify(ServerMsg::EntityCharacterState {
|
||||||
entity: uid.into(),
|
entity: uid.into(),
|
||||||
action_state,
|
character_state,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -928,84 +991,7 @@ impl Server {
|
|||||||
self.clients
|
self.clients
|
||||||
.notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
|
.notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
|
||||||
|
|
||||||
// TODO: Move this into some new method like `handle_sys_outputs` right after ticking the world
|
|
||||||
// Handle deaths.
|
|
||||||
let ecs = self.state.ecs_mut();
|
let ecs = self.state.ecs_mut();
|
||||||
let clients = &mut self.clients;
|
|
||||||
let todo_kill = (&ecs.entities(), &ecs.read_storage::<comp::Dying>())
|
|
||||||
.join()
|
|
||||||
.map(|(entity, dying)| {
|
|
||||||
// Chat message
|
|
||||||
if let Some(player) = ecs.read_storage::<comp::Player>().get(entity) {
|
|
||||||
let msg = if let comp::HealthSource::Attack { by } = dying.cause {
|
|
||||||
ecs.entity_from_uid(by.into()).and_then(|attacker| {
|
|
||||||
ecs.read_storage::<comp::Player>()
|
|
||||||
.get(attacker)
|
|
||||||
.map(|attacker_alias| {
|
|
||||||
format!(
|
|
||||||
"{} was killed by {}",
|
|
||||||
&player.alias, &attacker_alias.alias
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
.unwrap_or(format!("{} died", &player.alias));
|
|
||||||
|
|
||||||
clients.notify_registered(ServerMsg::kill(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give EXP to the client
|
|
||||||
let mut stats = ecs.write_storage::<comp::Stats>();
|
|
||||||
if let Some(entity_stats) = stats.get(entity).cloned() {
|
|
||||||
if let comp::HealthSource::Attack { by } = dying.cause {
|
|
||||||
ecs.entity_from_uid(by.into()).map(|attacker| {
|
|
||||||
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
|
||||||
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
|
||||||
attacker_stats.exp.change_by(
|
|
||||||
entity_stats.health.maximum() as f64 / 10.0
|
|
||||||
+ entity_stats.level.level() as f64 * 10.0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entity
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Actually kill them
|
|
||||||
for entity in todo_kill {
|
|
||||||
if let Some(client) = self.clients.get_mut(&entity) {
|
|
||||||
let _ = ecs.write_storage().insert(entity, comp::Vel(Vec3::zero()));
|
|
||||||
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
|
|
||||||
client.force_state(ClientState::Dead);
|
|
||||||
} else {
|
|
||||||
let _ = ecs.delete_entity_synced(entity);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle respawns
|
|
||||||
let todo_respawn = (&ecs.entities(), &ecs.read_storage::<comp::Respawning>())
|
|
||||||
.join()
|
|
||||||
.map(|(entity, _)| entity)
|
|
||||||
.collect::<Vec<EcsEntity>>();
|
|
||||||
|
|
||||||
for entity in todo_respawn {
|
|
||||||
if let Some(client) = self.clients.get_mut(&entity) {
|
|
||||||
client.allow_state(ClientState::Character);
|
|
||||||
ecs.write_storage::<comp::Stats>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|stats| stats.revive());
|
|
||||||
ecs.write_storage::<comp::Pos>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|pos| pos.0.z += 20.0);
|
|
||||||
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync physics
|
// Sync physics
|
||||||
for (entity, &uid, &pos, force_update) in (
|
for (entity, &uid, &pos, force_update) in (
|
||||||
@ -1043,7 +1029,7 @@ impl Server {
|
|||||||
let mut last_pos = ecs.write_storage::<comp::Last<comp::Pos>>();
|
let mut last_pos = ecs.write_storage::<comp::Last<comp::Pos>>();
|
||||||
let mut last_vel = ecs.write_storage::<comp::Last<comp::Vel>>();
|
let mut last_vel = ecs.write_storage::<comp::Last<comp::Vel>>();
|
||||||
let mut last_ori = ecs.write_storage::<comp::Last<comp::Ori>>();
|
let mut last_ori = ecs.write_storage::<comp::Last<comp::Ori>>();
|
||||||
let mut last_action_state = ecs.write_storage::<comp::Last<comp::ActionState>>();
|
let mut last_character_state = ecs.write_storage::<comp::Last<comp::CharacterState>>();
|
||||||
|
|
||||||
if let Some(client_pos) = ecs.read_storage::<comp::Pos>().get(entity) {
|
if let Some(client_pos) = ecs.read_storage::<comp::Pos>().get(entity) {
|
||||||
if last_pos
|
if last_pos
|
||||||
@ -1099,16 +1085,19 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(client_action_state) = ecs.read_storage::<comp::ActionState>().get(entity) {
|
if let Some(client_character_state) =
|
||||||
if last_action_state
|
ecs.read_storage::<comp::CharacterState>().get(entity)
|
||||||
|
{
|
||||||
|
if last_character_state
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.map(|&l| l != *client_action_state)
|
.map(|&l| l != *client_character_state)
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
let _ = last_action_state.insert(entity, comp::Last(*client_action_state));
|
let _ =
|
||||||
let msg = ServerMsg::EntityActionState {
|
last_character_state.insert(entity, comp::Last(*client_character_state));
|
||||||
|
let msg = ServerMsg::EntityCharacterState {
|
||||||
entity: uid.into(),
|
entity: uid.into(),
|
||||||
action_state: *client_action_state,
|
character_state: *client_character_state,
|
||||||
};
|
};
|
||||||
match force_update {
|
match force_update {
|
||||||
Some(_) => clients.notify_ingame_if(msg, in_vd),
|
Some(_) => clients.notify_ingame_if(msg, in_vd),
|
||||||
|
Loading…
Reference in New Issue
Block a user