mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implement killing
Change animation history to animation Add attack input event Implement killing with ecs systems for damage and death Sync attack properly Sync deaths Former-commit-id: 72b5be7d65d9d3fcbef50d4836a6f06ec218d69e
This commit is contained in:
parent
3ddf4bddab
commit
b3e4ca0a5d
@ -2,6 +2,7 @@ use vek::*;
|
||||
|
||||
pub enum InputEvent {
|
||||
Jump,
|
||||
AttackStarted,
|
||||
}
|
||||
|
||||
pub struct Input {
|
||||
|
@ -38,7 +38,7 @@ pub struct Client {
|
||||
thread_pool: ThreadPool,
|
||||
|
||||
last_ping: f64,
|
||||
pub postbox: PostBox<ClientMsg, ServerMsg>,
|
||||
postbox: PostBox<ClientMsg, ServerMsg>,
|
||||
|
||||
last_server_ping: Instant,
|
||||
last_ping_delta: f64,
|
||||
@ -59,7 +59,7 @@ impl Client {
|
||||
let mut postbox = PostBox::to(addr)?;
|
||||
|
||||
// Wait for initial sync
|
||||
let (state, entity) = match postbox.next_message() {
|
||||
let (mut state, entity) = match postbox.next_message() {
|
||||
Some(ServerMsg::InitialSync {
|
||||
ecs_state,
|
||||
entity_uid,
|
||||
@ -74,6 +74,9 @@ impl Client {
|
||||
_ => return Err(Error::ServerWentMad),
|
||||
};
|
||||
|
||||
// Initialize ecs components the client has control over
|
||||
state.write_component(entity, comp::Actions::new());
|
||||
|
||||
Ok(Self {
|
||||
client_state,
|
||||
thread_pool: threadpool::Builder::new()
|
||||
@ -99,6 +102,11 @@ impl Client {
|
||||
self.postbox.send_message(ClientMsg::Register { player });
|
||||
}
|
||||
|
||||
pub fn request_character(&mut self, name: String, body: comp::Body) {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::Character { name, body });
|
||||
}
|
||||
|
||||
pub fn set_view_distance(&mut self, view_distance: u32) {
|
||||
self.view_distance = Some(view_distance.max(5).min(25));
|
||||
self.postbox
|
||||
@ -157,17 +165,40 @@ impl Client {
|
||||
// approximate order of things. Please update it as this code changes.
|
||||
//
|
||||
// 1) Collect input from the frontend, apply input effects to the state of the game
|
||||
// 2) Go through any events (timer-driven or otherwise) that need handling and apply them
|
||||
// 2) Handle messages from the server
|
||||
// 3) Go through any events (timer-driven or otherwise) that need handling and apply them
|
||||
// to the state of the game
|
||||
// 3) Perform a single LocalState tick (i.e: update the world and entities in the world)
|
||||
// 4) Go through the terrain update queue and apply all changes to the terrain
|
||||
// 5) Finish the tick, passing control of the main thread back to the frontend
|
||||
// 4) Perform a single LocalState tick (i.e: update the world and entities in the world)
|
||||
// 5) Go through the terrain update queue and apply all changes to the terrain
|
||||
// 6) Sync information to the server
|
||||
// 7) Finish the tick, passing control of the main thread back to the frontend
|
||||
|
||||
// Build up a list of events for this frame, to be passed to the frontend.
|
||||
let mut frontend_events = Vec::new();
|
||||
// 1) Handle input from frontend.
|
||||
for event in input.events {
|
||||
match event {
|
||||
InputEvent::AttackStarted => {
|
||||
self.state
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::Actions>()
|
||||
.get_mut(self.entity)
|
||||
.unwrap() // We initialized it in the constructor
|
||||
.0
|
||||
.push(comp::Action::Attack);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new messages from the server.
|
||||
frontend_events.append(&mut self.handle_new_messages()?);
|
||||
// Tell the server about the actions.
|
||||
if let Some(actions) = self
|
||||
.state
|
||||
.ecs()
|
||||
.read_storage::<comp::Actions>()
|
||||
.get(self.entity)
|
||||
{
|
||||
self.postbox
|
||||
.send_message(ClientMsg::PlayerActions(actions.clone()));
|
||||
}
|
||||
|
||||
// Pass character control from frontend input to the player's entity.
|
||||
// TODO: Only do this if the entity already has a Control component!
|
||||
@ -180,35 +211,18 @@ impl Client {
|
||||
},
|
||||
);
|
||||
|
||||
// Tick the client's LocalState (step 3).
|
||||
// 2) Build up a list of events for this frame, to be passed to the frontend.
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
// Handle new messages from the server.
|
||||
frontend_events.append(&mut self.handle_new_messages()?);
|
||||
|
||||
// 3)
|
||||
|
||||
// 4) Tick the client's LocalState
|
||||
self.state.tick(dt);
|
||||
|
||||
// Update the server about the player's physics attributes.
|
||||
match (
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
) {
|
||||
(Some(pos), Some(vel), Some(dir)) => {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::PlayerPhysics { pos, vel, dir });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Update the server about the player's currently playing animation and the previous one.
|
||||
if let Some(animation_history) = self
|
||||
.state
|
||||
.read_storage::<comp::AnimationHistory>()
|
||||
.get(self.entity)
|
||||
.cloned()
|
||||
{
|
||||
if Some(animation_history.current) != animation_history.last {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::PlayerAnimation(animation_history));
|
||||
}
|
||||
}
|
||||
|
||||
// 5) Terrain
|
||||
let pos = self
|
||||
.state
|
||||
.read_storage::<comp::phys::Pos>()
|
||||
@ -259,13 +273,39 @@ impl Client {
|
||||
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(10));
|
||||
}
|
||||
|
||||
// send a ping to the server once every second
|
||||
// Send a ping to the server once every second
|
||||
if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) {
|
||||
self.postbox.send_message(ClientMsg::Ping);
|
||||
self.last_server_ping = Instant::now();
|
||||
}
|
||||
|
||||
// Finish the tick, pass control back to the frontend (step 6).
|
||||
// 6) Update the server about the player's physics attributes.
|
||||
match (
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
self.state.read_storage().get(self.entity).cloned(),
|
||||
) {
|
||||
(Some(pos), Some(vel), Some(dir)) => {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::PlayerPhysics { pos, vel, dir });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Update the server about the player's current animation.
|
||||
if let Some(mut animation_info) = self
|
||||
.state
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::AnimationInfo>()
|
||||
.get_mut(self.entity)
|
||||
{
|
||||
if animation_info.changed {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::PlayerAnimation(animation_info.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
// 7) Finish the tick, pass control back to the frontend.
|
||||
self.tick += 1;
|
||||
Ok(frontend_events)
|
||||
}
|
||||
@ -319,10 +359,10 @@ impl Client {
|
||||
},
|
||||
ServerMsg::EntityAnimation {
|
||||
entity,
|
||||
animation_history,
|
||||
animation_info,
|
||||
} => match self.state.ecs().entity_from_uid(entity) {
|
||||
Some(entity) => {
|
||||
self.state.write_component(entity, animation_history);
|
||||
self.state.write_component(entity, animation_info);
|
||||
}
|
||||
None => {}
|
||||
},
|
||||
|
20
common/src/comp/action.rs
Normal file
20
common/src/comp/action.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use specs::{Component, FlaggedStorage, VecStorage};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Action {
|
||||
Attack,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Actions(pub Vec<Action>);
|
||||
|
||||
impl Actions {
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Actions {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
@ -229,33 +229,3 @@ pub enum Actor {
|
||||
impl Component for Actor {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct AnimationHistory {
|
||||
pub last: Option<Animation>,
|
||||
pub current: Animation,
|
||||
pub time: f64,
|
||||
}
|
||||
|
||||
impl AnimationHistory {
|
||||
pub fn new(animation: Animation) -> Self {
|
||||
Self {
|
||||
last: None,
|
||||
current: animation,
|
||||
time: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Animation {
|
||||
Idle,
|
||||
Run,
|
||||
Jump,
|
||||
Gliding,
|
||||
Attack,
|
||||
}
|
||||
|
||||
impl Component for AnimationHistory {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
||||
|
32
common/src/comp/animation.rs
Normal file
32
common/src/comp/animation.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use specs::{Component, FlaggedStorage, VecStorage};
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Animation {
|
||||
Idle,
|
||||
Run,
|
||||
Jump,
|
||||
Gliding,
|
||||
Attack,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct AnimationInfo {
|
||||
pub animation: Animation,
|
||||
pub time: f64,
|
||||
pub changed: bool,
|
||||
}
|
||||
|
||||
impl AnimationInfo {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
animation: Animation::Idle,
|
||||
time: 0.0,
|
||||
changed: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for AnimationInfo {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
@ -1,16 +1,21 @@
|
||||
pub mod action;
|
||||
pub mod actor;
|
||||
pub mod agent;
|
||||
pub mod animation;
|
||||
pub mod phys;
|
||||
pub mod player;
|
||||
pub mod stats;
|
||||
|
||||
// Reexports
|
||||
pub use action::Action;
|
||||
pub use action::Actions;
|
||||
pub use actor::Actor;
|
||||
pub use actor::Animation;
|
||||
pub use actor::AnimationHistory;
|
||||
pub use actor::Body;
|
||||
pub use actor::HumanoidBody;
|
||||
pub use actor::QuadrupedBody;
|
||||
pub use agent::{Agent, Control};
|
||||
pub use animation::Animation;
|
||||
pub use animation::AnimationInfo;
|
||||
pub use player::Player;
|
||||
pub use stats::Dying;
|
||||
pub use stats::Stats;
|
||||
|
@ -28,7 +28,7 @@ impl Component for Dir {
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
||||
// Update
|
||||
// ForceUpdate
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct ForceUpdate;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use specs::{Component, FlaggedStorage, VecStorage};
|
||||
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Health {
|
||||
pub current: u32,
|
||||
pub maximum: u32,
|
||||
@ -14,7 +14,7 @@ impl Health {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Stats {
|
||||
pub hp: Health,
|
||||
pub xp: u32,
|
||||
@ -36,3 +36,10 @@ impl Default for Stats {
|
||||
impl Component for Stats {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Dying;
|
||||
|
||||
impl Component for Dying {
|
||||
type Storage = NullStorage<Self>;
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ pub enum ClientMsg {
|
||||
Ping,
|
||||
Pong,
|
||||
Chat(String),
|
||||
PlayerAnimation(comp::AnimationHistory),
|
||||
PlayerActions(comp::Actions),
|
||||
PlayerAnimation(comp::AnimationInfo),
|
||||
PlayerPhysics {
|
||||
pos: comp::phys::Pos,
|
||||
vel: comp::phys::Vel,
|
||||
|
@ -31,7 +31,7 @@ pub enum ServerMsg {
|
||||
},
|
||||
EntityAnimation {
|
||||
entity: u64,
|
||||
animation_history: comp::AnimationHistory,
|
||||
animation_info: comp::AnimationInfo,
|
||||
},
|
||||
TerrainChunkUpdate {
|
||||
key: Vec2<i32>,
|
||||
|
@ -108,7 +108,9 @@ impl State {
|
||||
ecs.register::<comp::phys::Pos>();
|
||||
ecs.register::<comp::phys::Vel>();
|
||||
ecs.register::<comp::phys::Dir>();
|
||||
ecs.register::<comp::AnimationHistory>();
|
||||
ecs.register::<comp::AnimationInfo>();
|
||||
ecs.register::<comp::Actions>();
|
||||
ecs.register::<comp::Dying>();
|
||||
ecs.register::<comp::Agent>();
|
||||
ecs.register::<comp::Control>();
|
||||
ecs.register::<inventory::Inventory>();
|
||||
|
42
common/src/sys/action.rs
Normal file
42
common/src/sys/action.rs
Normal file
@ -0,0 +1,42 @@
|
||||
// Library
|
||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
comp::{phys::Pos, Action, Actions, Control, Stats},
|
||||
state::DeltaTime,
|
||||
};
|
||||
|
||||
// Basic ECS AI agent system
|
||||
pub struct Sys;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, DeltaTime>,
|
||||
WriteStorage<'a, Actions>,
|
||||
ReadStorage<'a, Pos>,
|
||||
WriteStorage<'a, Stats>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (entities, dt, mut actions, positions, mut stats): Self::SystemData) {
|
||||
for (a, mut actions_a, pos_a) in (&entities, &mut actions, &positions).join() {
|
||||
for event in actions_a.0.drain(..) {
|
||||
match event {
|
||||
Action::Attack => {
|
||||
for (b, pos_b, stat_b) in (&entities, &positions, &mut stats).join() {
|
||||
if a == b {
|
||||
continue;
|
||||
}
|
||||
if pos_a.0.distance_squared(pos_b.0) < 50.0 {
|
||||
&mut stat_b.hp.change_by(-60, 0.0); // TODO: variable damage and current time
|
||||
&stat_b.hp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Library
|
||||
use specs::{Join, Read, ReadStorage, System, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
comp::{phys::Pos, AnimationHistory, Control},
|
||||
state::DeltaTime,
|
||||
};
|
||||
|
||||
// Basic ECS AI agent system
|
||||
pub struct Sys;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (Read<'a, DeltaTime>, WriteStorage<'a, AnimationHistory>);
|
||||
|
||||
fn run(&mut self, (dt, mut anim_history): Self::SystemData) {
|
||||
for (mut anim_history) in (&mut anim_history).join() {
|
||||
anim_history.time += dt.0 as f64;
|
||||
}
|
||||
}
|
||||
}
|
25
common/src/sys/animation.rs
Normal file
25
common/src/sys/animation.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// Library
|
||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
comp::{phys::Pos, ActionState, ActionEvent, Control, Stats},
|
||||
state::DeltaTime,
|
||||
};
|
||||
|
||||
// Basic ECS AI agent system
|
||||
pub struct Sys;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, DeltaTime>,
|
||||
WriteStorage<'a, ActionState>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (dt, mut action_states): Self::SystemData) {
|
||||
for (dt, mut animation) in (dt, &mut animation).join() {
|
||||
animation.time += dt.0 as f64;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ use vek::*;
|
||||
use crate::{
|
||||
comp::{
|
||||
phys::{Dir, Pos, Vel},
|
||||
Animation, AnimationHistory, Control,
|
||||
Animation, AnimationInfo, Control,
|
||||
},
|
||||
state::DeltaTime,
|
||||
terrain::TerrainMap,
|
||||
@ -24,13 +24,13 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Pos>,
|
||||
WriteStorage<'a, Vel>,
|
||||
WriteStorage<'a, Dir>,
|
||||
WriteStorage<'a, AnimationHistory>,
|
||||
WriteStorage<'a, AnimationInfo>,
|
||||
ReadStorage<'a, Control>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(terrain, dt, entities, pos, mut vels, mut dirs, mut anims, controls): Self::SystemData,
|
||||
(terrain, dt, entities, pos, mut vels, mut dirs, mut animation_infos, controls): Self::SystemData,
|
||||
) {
|
||||
for (entity, pos, mut vel, mut dir, control) in
|
||||
(&entities, &pos, &mut vels, &mut dirs, &controls).join()
|
||||
@ -94,22 +94,18 @@ impl<'a> System<'a> for Sys {
|
||||
Animation::Jump
|
||||
};
|
||||
|
||||
let last_history = anims.get_mut(entity).cloned();
|
||||
let last = animation_infos
|
||||
.get_mut(entity)
|
||||
.cloned()
|
||||
.unwrap_or(AnimationInfo::new());
|
||||
let changed = last.animation != animation;
|
||||
|
||||
let time = if let Some((true, time)) =
|
||||
last_history.map(|last| (last.current == animation, last.time))
|
||||
{
|
||||
time
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
anims.insert(
|
||||
animation_infos.insert(
|
||||
entity,
|
||||
AnimationHistory {
|
||||
last: last_history.map(|last| last.current),
|
||||
current: animation,
|
||||
time,
|
||||
AnimationInfo {
|
||||
animation,
|
||||
time: if changed { 0.0 } else { last.time },
|
||||
changed,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
pub mod action;
|
||||
pub mod agent;
|
||||
pub mod anim;
|
||||
pub mod control;
|
||||
pub mod phys;
|
||||
mod stats;
|
||||
|
||||
// External
|
||||
use specs::DispatcherBuilder;
|
||||
@ -11,10 +12,16 @@ const AGENT_SYS: &str = "agent_sys";
|
||||
const CONTROL_SYS: &str = "control_sys";
|
||||
const PHYS_SYS: &str = "phys_sys";
|
||||
const ANIM_SYS: &str = "anim_sys";
|
||||
const MOVEMENT_SYS: &str = "movement_sys";
|
||||
const ACTION_SYS: &str = "action_sys";
|
||||
const STATS_SYS: &str = "stats_sys";
|
||||
|
||||
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
|
||||
dispatch_builder.add(phys::Sys, PHYS_SYS, &[]);
|
||||
dispatch_builder.add(control::Sys, CONTROL_SYS, &["phys_sys"]);
|
||||
dispatch_builder.add(control::Sys, CONTROL_SYS, &[PHYS_SYS]);
|
||||
dispatch_builder.add(anim::Sys, ANIM_SYS, &[]);
|
||||
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
|
||||
dispatch_builder.add(action::Sys, ACTION_SYS, &[]);
|
||||
dispatch_builder.add(stats::Sys, STATS_SYS, &[ACTION_SYS]);
|
||||
}
|
||||
|
28
common/src/sys/stats.rs
Normal file
28
common/src/sys/stats.rs
Normal file
@ -0,0 +1,28 @@
|
||||
// Library
|
||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use vek::*;
|
||||
|
||||
// Crate
|
||||
use crate::{
|
||||
comp::{Control, Dying, Stats},
|
||||
state::DeltaTime,
|
||||
};
|
||||
|
||||
// Basic ECS AI agent system
|
||||
pub struct Sys;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
ReadStorage<'a, Stats>,
|
||||
WriteStorage<'a, Dying>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (entities, stats, mut dyings): Self::SystemData) {
|
||||
for (entity, stat) in (&entities, &stats).join() {
|
||||
if stat.hp.current == 0 {
|
||||
dyings.insert(entity, Dying);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -134,7 +134,7 @@ impl Server {
|
||||
.with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)))
|
||||
.with(comp::phys::Vel(Vec3::zero()))
|
||||
.with(comp::phys::Dir(Vec3::unit_y()))
|
||||
.with(comp::AnimationHistory::new(comp::Animation::Idle))
|
||||
.with(comp::Actions::new())
|
||||
.with(comp::Actor::Character { name, body })
|
||||
.with(comp::Stats::default())
|
||||
}
|
||||
@ -156,7 +156,10 @@ impl Server {
|
||||
state.write_component(entity, comp::phys::ForceUpdate);
|
||||
|
||||
// Set initial animation.
|
||||
state.write_component(entity, comp::AnimationHistory::new(comp::Animation::Idle));
|
||||
state.write_component(entity, comp::AnimationInfo::new());
|
||||
|
||||
// Set initial actions (none).
|
||||
state.write_component(entity, comp::Actions::new());
|
||||
|
||||
// Tell the client its request was successful.
|
||||
client.notify(ServerMsg::StateAnswer(Ok(ClientState::Character)));
|
||||
@ -180,7 +183,7 @@ impl Server {
|
||||
// 6) Send relevant state updates to all clients
|
||||
// 7) Finish the tick, passing control of the main thread back to the frontend
|
||||
|
||||
// Build up a list of events for this frame, to be passed to the frontend.
|
||||
// 1) Build up a list of events for this frame, to be passed to the frontend.
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
// If networking has problems, handle them.
|
||||
@ -188,19 +191,19 @@ impl Server {
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
// Handle new client connections (step 2).
|
||||
frontend_events.append(&mut self.handle_new_connections()?);
|
||||
// 2)
|
||||
|
||||
// Handle new messages from clients
|
||||
// 3) Handle inputs from clients
|
||||
frontend_events.append(&mut self.handle_new_connections()?);
|
||||
frontend_events.append(&mut self.handle_new_messages()?);
|
||||
|
||||
// Tick the client's LocalState (step 3).
|
||||
// 4) Tick the client's LocalState.
|
||||
self.state.tick(dt);
|
||||
|
||||
// Tick the world
|
||||
self.world.tick(dt);
|
||||
|
||||
// Fetch any generated `TerrainChunk`s and insert them into the terrain.
|
||||
// 5) Fetch any generated `TerrainChunk`s and insert them into the terrain.
|
||||
// Also, send the chunk data to anybody that is close by.
|
||||
if let Ok((key, chunk)) = self.chunk_rx.try_recv() {
|
||||
// Send the chunk to all nearby players.
|
||||
@ -261,10 +264,10 @@ impl Server {
|
||||
self.state.remove_chunk(key);
|
||||
}
|
||||
|
||||
// Synchronise clients with the new state of the world.
|
||||
// 6) Synchronise clients with the new state of the world.
|
||||
self.sync_clients();
|
||||
|
||||
// Finish the tick, pass control back to the frontend (step 6).
|
||||
// 7) Finish the tick, pass control back to the frontend.
|
||||
Ok(frontend_events)
|
||||
}
|
||||
|
||||
@ -389,10 +392,19 @@ impl Server {
|
||||
| ClientState::Spectator
|
||||
| ClientState::Character => new_chat_msgs.push((entity, msg)),
|
||||
},
|
||||
ClientMsg::PlayerAnimation(animation_history) => {
|
||||
ClientMsg::PlayerActions(mut actions) => {
|
||||
state
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::Actions>()
|
||||
.get_mut(entity)
|
||||
.map(|s| {
|
||||
s.0.append(&mut actions.0);
|
||||
});
|
||||
}
|
||||
ClientMsg::PlayerAnimation(animation_info) => {
|
||||
match client.client_state {
|
||||
ClientState::Character => {
|
||||
state.write_component(entity, animation_history)
|
||||
state.write_component(entity, animation_info)
|
||||
}
|
||||
// Only characters can send animations.
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
@ -492,17 +504,17 @@ impl Server {
|
||||
state.write_component(entity, player);
|
||||
|
||||
// Sync logical information other players have authority over, not the server.
|
||||
for (other_entity, &uid, &animation_history) in (
|
||||
for (other_entity, &uid, &animation_info) in (
|
||||
&state.ecs().entities(),
|
||||
&state.ecs().read_storage::<common::state::Uid>(),
|
||||
&state.ecs().read_storage::<comp::AnimationHistory>(),
|
||||
&state.ecs().read_storage::<comp::AnimationInfo>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// AnimationHistory
|
||||
// Animation
|
||||
client.postbox.send_message(ServerMsg::EntityAnimation {
|
||||
entity: uid.into(),
|
||||
animation_history: animation_history,
|
||||
animation_info: animation_info,
|
||||
});
|
||||
}
|
||||
|
||||
@ -543,36 +555,36 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
// Sync animation states.
|
||||
for (entity, &uid, &animation_history) in (
|
||||
// Sync animation.
|
||||
for (entity, &uid, &animation_info) in (
|
||||
&self.state.ecs().entities(),
|
||||
&self.state.ecs().read_storage::<Uid>(),
|
||||
&self.state.ecs().read_storage::<comp::AnimationHistory>(),
|
||||
&self.state.ecs().read_storage::<comp::AnimationInfo>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// Check if we need to sync.
|
||||
if Some(animation_history.current) == animation_history.last {
|
||||
continue;
|
||||
if animation_info.changed {
|
||||
self.clients.notify_ingame_except(
|
||||
entity,
|
||||
ServerMsg::EntityAnimation {
|
||||
entity: uid.into(),
|
||||
animation_info: animation_info.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
self.clients.notify_ingame_except(
|
||||
entity,
|
||||
ServerMsg::EntityAnimation {
|
||||
entity: uid.into(),
|
||||
animation_history,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Update animation last/current state.
|
||||
for (entity, mut animation_history) in (
|
||||
// Sync deaths.
|
||||
let todo_remove = (
|
||||
&self.state.ecs().entities(),
|
||||
&mut self.state.ecs().write_storage::<comp::AnimationHistory>(),
|
||||
&self.state.ecs().read_storage::<comp::Dying>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
animation_history.last = Some(animation_history.current);
|
||||
.map(|(entity, _)| entity)
|
||||
.collect::<Vec<EcsEntity>>();
|
||||
|
||||
for entity in todo_remove {
|
||||
self.state.ecs_mut().delete_entity_synced(entity);
|
||||
}
|
||||
|
||||
// Remove all force flags.
|
||||
|
@ -74,13 +74,10 @@ impl PlayState for CharSelectionState {
|
||||
return PlayStateResult::Pop;
|
||||
}
|
||||
ui::Event::Play => {
|
||||
self.client
|
||||
.borrow_mut()
|
||||
.postbox
|
||||
.send_message(ClientMsg::Character {
|
||||
name: self.char_selection_ui.character_name.clone(),
|
||||
body: comp::Body::Humanoid(self.char_selection_ui.character_body),
|
||||
});
|
||||
self.client.borrow_mut().request_character(
|
||||
self.char_selection_ui.character_name.clone(),
|
||||
comp::Body::Humanoid(self.char_selection_ui.character_body),
|
||||
);
|
||||
return PlayStateResult::Switch(Box::new(SessionState::new(
|
||||
&mut global_state.window,
|
||||
self.client.clone(),
|
||||
|
@ -347,13 +347,13 @@ impl FigureMgr {
|
||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||
let time = client.state().get_time();
|
||||
let ecs = client.state().ecs();
|
||||
for (entity, pos, vel, dir, actor, animation_history, stats) in (
|
||||
for (entity, pos, vel, dir, actor, animation_info, stats) in (
|
||||
&ecs.entities(),
|
||||
&ecs.read_storage::<comp::phys::Pos>(),
|
||||
&ecs.read_storage::<comp::phys::Vel>(),
|
||||
&ecs.read_storage::<comp::phys::Dir>(),
|
||||
&ecs.read_storage::<comp::Actor>(),
|
||||
&ecs.read_storage::<comp::AnimationHistory>(),
|
||||
&ecs.read_storage::<comp::AnimationInfo>(),
|
||||
ecs.read_storage::<comp::Stats>().maybe(),
|
||||
)
|
||||
.join()
|
||||
@ -365,21 +365,21 @@ impl FigureMgr {
|
||||
FigureState::new(renderer, CharacterSkeleton::new())
|
||||
});
|
||||
|
||||
let target_skeleton = match animation_history.current {
|
||||
let target_skeleton = match animation_info.animation {
|
||||
comp::Animation::Idle => character::IdleAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
animation_info.time,
|
||||
),
|
||||
comp::Animation::Run => character::RunAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
animation_info.time,
|
||||
),
|
||||
comp::Animation::Jump => character::JumpAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
animation_info.time,
|
||||
),
|
||||
comp::Animation::Attack => character::AttackAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
@ -390,7 +390,7 @@ impl FigureMgr {
|
||||
character::GlidingAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
animation_info.time,
|
||||
)
|
||||
}
|
||||
};
|
||||
@ -404,21 +404,21 @@ impl FigureMgr {
|
||||
FigureState::new(renderer, QuadrupedSkeleton::new())
|
||||
});
|
||||
|
||||
let target_skeleton = match animation_history.current {
|
||||
let target_skeleton = match animation_info.animation {
|
||||
comp::Animation::Run => quadruped::RunAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
animation_info.time,
|
||||
),
|
||||
comp::Animation::Idle => quadruped::IdleAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
time,
|
||||
animation_history.time,
|
||||
animation_info.time,
|
||||
),
|
||||
comp::Animation::Jump => quadruped::JumpAnimation::update_skeleton(
|
||||
state.skeleton_mut(),
|
||||
(vel.0.magnitude(), time),
|
||||
animation_history.time,
|
||||
animation_info.time,
|
||||
),
|
||||
|
||||
// TODO!
|
||||
|
@ -9,6 +9,7 @@ use crate::{
|
||||
};
|
||||
use client::{self, Client, Input, InputEvent};
|
||||
use common::clock::Clock;
|
||||
use glutin::MouseButton;
|
||||
use std::{cell::RefCell, mem, rc::Rc, time::Duration};
|
||||
use vek::*;
|
||||
|
||||
@ -138,7 +139,11 @@ impl PlayState for SessionState {
|
||||
Event::Close => {
|
||||
return PlayStateResult::Shutdown;
|
||||
}
|
||||
// Movement Key Pressed
|
||||
// Attack key pressed
|
||||
Event::Click(MouseButton::Left, state) => {
|
||||
self.input_events.push(InputEvent::AttackStarted)
|
||||
}
|
||||
// Movement key pressed
|
||||
Event::KeyDown(Key::MoveForward) => self.key_state.up = true,
|
||||
Event::KeyDown(Key::MoveBack) => self.key_state.down = true,
|
||||
Event::KeyDown(Key::MoveLeft) => self.key_state.left = true,
|
||||
@ -148,7 +153,7 @@ impl PlayState for SessionState {
|
||||
self.key_state.jump = true;
|
||||
}
|
||||
Event::KeyDown(Key::Glide) => self.key_state.glide = true,
|
||||
// Movement Key Released
|
||||
// Movement key released
|
||||
Event::KeyUp(Key::MoveForward) => self.key_state.up = false,
|
||||
Event::KeyUp(Key::MoveBack) => self.key_state.down = false,
|
||||
Event::KeyUp(Key::MoveLeft) => self.key_state.left = false,
|
||||
|
@ -3,6 +3,7 @@ use crate::{
|
||||
settings::Settings,
|
||||
ui, Error,
|
||||
};
|
||||
use glutin::{ElementState, MouseButton};
|
||||
use std::collections::HashMap;
|
||||
use vek::*;
|
||||
|
||||
@ -124,7 +125,11 @@ impl Window {
|
||||
events.push(Event::Resize(Vec2::new(width as u32, height as u32)));
|
||||
}
|
||||
glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)),
|
||||
|
||||
glutin::WindowEvent::MouseInput { button, state, .. }
|
||||
if cursor_grabbed && state == ElementState::Pressed =>
|
||||
{
|
||||
events.push(Event::Click(button, state))
|
||||
}
|
||||
glutin::WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode
|
||||
{
|
||||
Some(keycode) => match key_map.get(&keycode) {
|
||||
@ -292,6 +297,7 @@ pub enum Event {
|
||||
/// A key has been typed that corresponds to a specific character.
|
||||
Char(char),
|
||||
/// The cursor has been panned across the screen while grabbed.
|
||||
Click(MouseButton, ElementState),
|
||||
CursorPan(Vec2<f32>),
|
||||
/// The camera has been requested to zoom.
|
||||
Zoom(f32),
|
||||
|
Loading…
Reference in New Issue
Block a user