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:
@ -2,6 +2,7 @@ use vek::*;
|
|||||||
|
|
||||||
pub enum InputEvent {
|
pub enum InputEvent {
|
||||||
Jump,
|
Jump,
|
||||||
|
AttackStarted,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
|
@ -38,7 +38,7 @@ pub struct Client {
|
|||||||
thread_pool: ThreadPool,
|
thread_pool: ThreadPool,
|
||||||
|
|
||||||
last_ping: f64,
|
last_ping: f64,
|
||||||
pub postbox: PostBox<ClientMsg, ServerMsg>,
|
postbox: PostBox<ClientMsg, ServerMsg>,
|
||||||
|
|
||||||
last_server_ping: Instant,
|
last_server_ping: Instant,
|
||||||
last_ping_delta: f64,
|
last_ping_delta: f64,
|
||||||
@ -59,7 +59,7 @@ impl Client {
|
|||||||
let mut postbox = PostBox::to(addr)?;
|
let mut postbox = PostBox::to(addr)?;
|
||||||
|
|
||||||
// Wait for initial sync
|
// Wait for initial sync
|
||||||
let (state, entity) = match postbox.next_message() {
|
let (mut state, entity) = match postbox.next_message() {
|
||||||
Some(ServerMsg::InitialSync {
|
Some(ServerMsg::InitialSync {
|
||||||
ecs_state,
|
ecs_state,
|
||||||
entity_uid,
|
entity_uid,
|
||||||
@ -74,6 +74,9 @@ impl Client {
|
|||||||
_ => return Err(Error::ServerWentMad),
|
_ => return Err(Error::ServerWentMad),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Initialize ecs components the client has control over
|
||||||
|
state.write_component(entity, comp::Actions::new());
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client_state,
|
client_state,
|
||||||
thread_pool: threadpool::Builder::new()
|
thread_pool: threadpool::Builder::new()
|
||||||
@ -99,6 +102,11 @@ impl Client {
|
|||||||
self.postbox.send_message(ClientMsg::Register { player });
|
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) {
|
pub fn set_view_distance(&mut self, view_distance: u32) {
|
||||||
self.view_distance = Some(view_distance.max(5).min(25));
|
self.view_distance = Some(view_distance.max(5).min(25));
|
||||||
self.postbox
|
self.postbox
|
||||||
@ -157,17 +165,40 @@ impl Client {
|
|||||||
// approximate order of things. Please update it as this code changes.
|
// 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
|
// 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
|
// to the state of the game
|
||||||
// 3) Perform a single LocalState tick (i.e: update the world and entities in the world)
|
// 4) 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) 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
|
// 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.
|
// 1) Handle input from frontend.
|
||||||
let mut frontend_events = Vec::new();
|
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.
|
// Tell the server about the actions.
|
||||||
frontend_events.append(&mut self.handle_new_messages()?);
|
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.
|
// Pass character control from frontend input to the player's entity.
|
||||||
// TODO: Only do this if the entity already has a Control component!
|
// 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);
|
self.state.tick(dt);
|
||||||
|
|
||||||
// Update the server about the player's physics attributes.
|
// 5) Terrain
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pos = self
|
let pos = self
|
||||||
.state
|
.state
|
||||||
.read_storage::<comp::phys::Pos>()
|
.read_storage::<comp::phys::Pos>()
|
||||||
@ -259,13 +273,39 @@ impl Client {
|
|||||||
.retain(|_, created| now.duration_since(*created) < Duration::from_secs(10));
|
.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) {
|
if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) {
|
||||||
self.postbox.send_message(ClientMsg::Ping);
|
self.postbox.send_message(ClientMsg::Ping);
|
||||||
self.last_server_ping = Instant::now();
|
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;
|
self.tick += 1;
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
@ -319,10 +359,10 @@ impl Client {
|
|||||||
},
|
},
|
||||||
ServerMsg::EntityAnimation {
|
ServerMsg::EntityAnimation {
|
||||||
entity,
|
entity,
|
||||||
animation_history,
|
animation_info,
|
||||||
} => match self.state.ecs().entity_from_uid(entity) {
|
} => match self.state.ecs().entity_from_uid(entity) {
|
||||||
Some(entity) => {
|
Some(entity) => {
|
||||||
self.state.write_component(entity, animation_history);
|
self.state.write_component(entity, animation_info);
|
||||||
}
|
}
|
||||||
None => {}
|
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 {
|
impl Component for Actor {
|
||||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
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 actor;
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
|
pub mod animation;
|
||||||
pub mod phys;
|
pub mod phys;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
|
pub use action::Action;
|
||||||
|
pub use action::Actions;
|
||||||
pub use actor::Actor;
|
pub use actor::Actor;
|
||||||
pub use actor::Animation;
|
|
||||||
pub use actor::AnimationHistory;
|
|
||||||
pub use actor::Body;
|
pub use actor::Body;
|
||||||
pub use actor::HumanoidBody;
|
pub use actor::HumanoidBody;
|
||||||
pub use actor::QuadrupedBody;
|
pub use actor::QuadrupedBody;
|
||||||
pub use agent::{Agent, Control};
|
pub use agent::{Agent, Control};
|
||||||
|
pub use animation::Animation;
|
||||||
|
pub use animation::AnimationInfo;
|
||||||
pub use player::Player;
|
pub use player::Player;
|
||||||
|
pub use stats::Dying;
|
||||||
pub use stats::Stats;
|
pub use stats::Stats;
|
||||||
|
@ -28,7 +28,7 @@ impl Component for Dir {
|
|||||||
type Storage = VecStorage<Self>;
|
type Storage = VecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
// ForceUpdate
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct ForceUpdate;
|
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 struct Health {
|
||||||
pub current: u32,
|
pub current: u32,
|
||||||
pub maximum: 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 struct Stats {
|
||||||
pub hp: Health,
|
pub hp: Health,
|
||||||
pub xp: u32,
|
pub xp: u32,
|
||||||
@ -36,3 +36,10 @@ impl Default for Stats {
|
|||||||
impl Component for Stats {
|
impl Component for Stats {
|
||||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
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,
|
Ping,
|
||||||
Pong,
|
Pong,
|
||||||
Chat(String),
|
Chat(String),
|
||||||
PlayerAnimation(comp::AnimationHistory),
|
PlayerActions(comp::Actions),
|
||||||
|
PlayerAnimation(comp::AnimationInfo),
|
||||||
PlayerPhysics {
|
PlayerPhysics {
|
||||||
pos: comp::phys::Pos,
|
pos: comp::phys::Pos,
|
||||||
vel: comp::phys::Vel,
|
vel: comp::phys::Vel,
|
||||||
|
@ -31,7 +31,7 @@ pub enum ServerMsg {
|
|||||||
},
|
},
|
||||||
EntityAnimation {
|
EntityAnimation {
|
||||||
entity: u64,
|
entity: u64,
|
||||||
animation_history: comp::AnimationHistory,
|
animation_info: comp::AnimationInfo,
|
||||||
},
|
},
|
||||||
TerrainChunkUpdate {
|
TerrainChunkUpdate {
|
||||||
key: Vec2<i32>,
|
key: Vec2<i32>,
|
||||||
|
@ -108,7 +108,9 @@ impl State {
|
|||||||
ecs.register::<comp::phys::Pos>();
|
ecs.register::<comp::phys::Pos>();
|
||||||
ecs.register::<comp::phys::Vel>();
|
ecs.register::<comp::phys::Vel>();
|
||||||
ecs.register::<comp::phys::Dir>();
|
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::Agent>();
|
||||||
ecs.register::<comp::Control>();
|
ecs.register::<comp::Control>();
|
||||||
ecs.register::<inventory::Inventory>();
|
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::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
phys::{Dir, Pos, Vel},
|
phys::{Dir, Pos, Vel},
|
||||||
Animation, AnimationHistory, Control,
|
Animation, AnimationInfo, Control,
|
||||||
},
|
},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
terrain::TerrainMap,
|
terrain::TerrainMap,
|
||||||
@ -24,13 +24,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Dir>,
|
WriteStorage<'a, Dir>,
|
||||||
WriteStorage<'a, AnimationHistory>,
|
WriteStorage<'a, AnimationInfo>,
|
||||||
ReadStorage<'a, Control>,
|
ReadStorage<'a, Control>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&mut self,
|
&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
|
for (entity, pos, mut vel, mut dir, control) in
|
||||||
(&entities, &pos, &mut vels, &mut dirs, &controls).join()
|
(&entities, &pos, &mut vels, &mut dirs, &controls).join()
|
||||||
@ -94,22 +94,18 @@ impl<'a> System<'a> for Sys {
|
|||||||
Animation::Jump
|
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)) =
|
animation_infos.insert(
|
||||||
last_history.map(|last| (last.current == animation, last.time))
|
|
||||||
{
|
|
||||||
time
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
anims.insert(
|
|
||||||
entity,
|
entity,
|
||||||
AnimationHistory {
|
AnimationInfo {
|
||||||
last: last_history.map(|last| last.current),
|
animation,
|
||||||
current: animation,
|
time: if changed { 0.0 } else { last.time },
|
||||||
time,
|
changed,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
pub mod action;
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
pub mod anim;
|
|
||||||
pub mod control;
|
pub mod control;
|
||||||
pub mod phys;
|
pub mod phys;
|
||||||
|
mod stats;
|
||||||
|
|
||||||
// External
|
// External
|
||||||
use specs::DispatcherBuilder;
|
use specs::DispatcherBuilder;
|
||||||
@ -11,10 +12,16 @@ const AGENT_SYS: &str = "agent_sys";
|
|||||||
const CONTROL_SYS: &str = "control_sys";
|
const CONTROL_SYS: &str = "control_sys";
|
||||||
const PHYS_SYS: &str = "phys_sys";
|
const PHYS_SYS: &str = "phys_sys";
|
||||||
const ANIM_SYS: &str = "anim_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) {
|
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||||
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
|
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
|
||||||
dispatch_builder.add(phys::Sys, PHYS_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(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::Pos(Vec3::new(0.0, 0.0, 64.0)))
|
||||||
.with(comp::phys::Vel(Vec3::zero()))
|
.with(comp::phys::Vel(Vec3::zero()))
|
||||||
.with(comp::phys::Dir(Vec3::unit_y()))
|
.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::Actor::Character { name, body })
|
||||||
.with(comp::Stats::default())
|
.with(comp::Stats::default())
|
||||||
}
|
}
|
||||||
@ -156,7 +156,10 @@ impl Server {
|
|||||||
state.write_component(entity, comp::phys::ForceUpdate);
|
state.write_component(entity, comp::phys::ForceUpdate);
|
||||||
|
|
||||||
// Set initial animation.
|
// 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.
|
// Tell the client its request was successful.
|
||||||
client.notify(ServerMsg::StateAnswer(Ok(ClientState::Character)));
|
client.notify(ServerMsg::StateAnswer(Ok(ClientState::Character)));
|
||||||
@ -180,7 +183,7 @@ impl Server {
|
|||||||
// 6) Send relevant state updates to all clients
|
// 6) Send relevant state updates to all clients
|
||||||
// 7) Finish the tick, passing control of the main thread back to the frontend
|
// 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();
|
let mut frontend_events = Vec::new();
|
||||||
|
|
||||||
// If networking has problems, handle them.
|
// If networking has problems, handle them.
|
||||||
@ -188,19 +191,19 @@ impl Server {
|
|||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle new client connections (step 2).
|
// 2)
|
||||||
frontend_events.append(&mut self.handle_new_connections()?);
|
|
||||||
|
|
||||||
// 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()?);
|
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);
|
self.state.tick(dt);
|
||||||
|
|
||||||
// Tick the world
|
// Tick the world
|
||||||
self.world.tick(dt);
|
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.
|
// Also, send the chunk data to anybody that is close by.
|
||||||
if let Ok((key, chunk)) = self.chunk_rx.try_recv() {
|
if let Ok((key, chunk)) = self.chunk_rx.try_recv() {
|
||||||
// Send the chunk to all nearby players.
|
// Send the chunk to all nearby players.
|
||||||
@ -261,10 +264,10 @@ impl Server {
|
|||||||
self.state.remove_chunk(key);
|
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();
|
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)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,10 +392,19 @@ impl Server {
|
|||||||
| ClientState::Spectator
|
| ClientState::Spectator
|
||||||
| ClientState::Character => new_chat_msgs.push((entity, msg)),
|
| 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 {
|
match client.client_state {
|
||||||
ClientState::Character => {
|
ClientState::Character => {
|
||||||
state.write_component(entity, animation_history)
|
state.write_component(entity, animation_info)
|
||||||
}
|
}
|
||||||
// Only characters can send animations.
|
// Only characters can send animations.
|
||||||
_ => client.error_state(RequestStateError::Impossible),
|
_ => client.error_state(RequestStateError::Impossible),
|
||||||
@ -492,17 +504,17 @@ impl Server {
|
|||||||
state.write_component(entity, player);
|
state.write_component(entity, player);
|
||||||
|
|
||||||
// Sync logical information other players have authority over, not the server.
|
// 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().entities(),
|
||||||
&state.ecs().read_storage::<common::state::Uid>(),
|
&state.ecs().read_storage::<common::state::Uid>(),
|
||||||
&state.ecs().read_storage::<comp::AnimationHistory>(),
|
&state.ecs().read_storage::<comp::AnimationInfo>(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
// AnimationHistory
|
// Animation
|
||||||
client.postbox.send_message(ServerMsg::EntityAnimation {
|
client.postbox.send_message(ServerMsg::EntityAnimation {
|
||||||
entity: uid.into(),
|
entity: uid.into(),
|
||||||
animation_history: animation_history,
|
animation_info: animation_info,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,36 +555,36 @@ impl Server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync animation states.
|
// Sync animation.
|
||||||
for (entity, &uid, &animation_history) in (
|
for (entity, &uid, &animation_info) in (
|
||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&self.state.ecs().read_storage::<Uid>(),
|
&self.state.ecs().read_storage::<Uid>(),
|
||||||
&self.state.ecs().read_storage::<comp::AnimationHistory>(),
|
&self.state.ecs().read_storage::<comp::AnimationInfo>(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
// Check if we need to sync.
|
if animation_info.changed {
|
||||||
if Some(animation_history.current) == animation_history.last {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.clients.notify_ingame_except(
|
self.clients.notify_ingame_except(
|
||||||
entity,
|
entity,
|
||||||
ServerMsg::EntityAnimation {
|
ServerMsg::EntityAnimation {
|
||||||
entity: uid.into(),
|
entity: uid.into(),
|
||||||
animation_history,
|
animation_info: animation_info.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update animation last/current state.
|
// Sync deaths.
|
||||||
for (entity, mut animation_history) in (
|
let todo_remove = (
|
||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&mut self.state.ecs().write_storage::<comp::AnimationHistory>(),
|
&self.state.ecs().read_storage::<comp::Dying>(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
.map(|(entity, _)| entity)
|
||||||
animation_history.last = Some(animation_history.current);
|
.collect::<Vec<EcsEntity>>();
|
||||||
|
|
||||||
|
for entity in todo_remove {
|
||||||
|
self.state.ecs_mut().delete_entity_synced(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all force flags.
|
// Remove all force flags.
|
||||||
|
@ -74,13 +74,10 @@ impl PlayState for CharSelectionState {
|
|||||||
return PlayStateResult::Pop;
|
return PlayStateResult::Pop;
|
||||||
}
|
}
|
||||||
ui::Event::Play => {
|
ui::Event::Play => {
|
||||||
self.client
|
self.client.borrow_mut().request_character(
|
||||||
.borrow_mut()
|
self.char_selection_ui.character_name.clone(),
|
||||||
.postbox
|
comp::Body::Humanoid(self.char_selection_ui.character_body),
|
||||||
.send_message(ClientMsg::Character {
|
);
|
||||||
name: self.char_selection_ui.character_name.clone(),
|
|
||||||
body: comp::Body::Humanoid(self.char_selection_ui.character_body),
|
|
||||||
});
|
|
||||||
return PlayStateResult::Switch(Box::new(SessionState::new(
|
return PlayStateResult::Switch(Box::new(SessionState::new(
|
||||||
&mut global_state.window,
|
&mut global_state.window,
|
||||||
self.client.clone(),
|
self.client.clone(),
|
||||||
|
@ -347,13 +347,13 @@ impl FigureMgr {
|
|||||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) {
|
||||||
let time = client.state().get_time();
|
let time = client.state().get_time();
|
||||||
let ecs = client.state().ecs();
|
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.entities(),
|
||||||
&ecs.read_storage::<comp::phys::Pos>(),
|
&ecs.read_storage::<comp::phys::Pos>(),
|
||||||
&ecs.read_storage::<comp::phys::Vel>(),
|
&ecs.read_storage::<comp::phys::Vel>(),
|
||||||
&ecs.read_storage::<comp::phys::Dir>(),
|
&ecs.read_storage::<comp::phys::Dir>(),
|
||||||
&ecs.read_storage::<comp::Actor>(),
|
&ecs.read_storage::<comp::Actor>(),
|
||||||
&ecs.read_storage::<comp::AnimationHistory>(),
|
&ecs.read_storage::<comp::AnimationInfo>(),
|
||||||
ecs.read_storage::<comp::Stats>().maybe(),
|
ecs.read_storage::<comp::Stats>().maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
@ -365,21 +365,21 @@ impl FigureMgr {
|
|||||||
FigureState::new(renderer, CharacterSkeleton::new())
|
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(
|
comp::Animation::Idle => character::IdleAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
time,
|
time,
|
||||||
animation_history.time,
|
animation_info.time,
|
||||||
),
|
),
|
||||||
comp::Animation::Run => character::RunAnimation::update_skeleton(
|
comp::Animation::Run => character::RunAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
(vel.0.magnitude(), time),
|
(vel.0.magnitude(), time),
|
||||||
animation_history.time,
|
animation_info.time,
|
||||||
),
|
),
|
||||||
comp::Animation::Jump => character::JumpAnimation::update_skeleton(
|
comp::Animation::Jump => character::JumpAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
time,
|
time,
|
||||||
animation_history.time,
|
animation_info.time,
|
||||||
),
|
),
|
||||||
comp::Animation::Attack => character::AttackAnimation::update_skeleton(
|
comp::Animation::Attack => character::AttackAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
@ -390,7 +390,7 @@ impl FigureMgr {
|
|||||||
character::GlidingAnimation::update_skeleton(
|
character::GlidingAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
time,
|
time,
|
||||||
animation_history.time,
|
animation_info.time,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -404,21 +404,21 @@ impl FigureMgr {
|
|||||||
FigureState::new(renderer, QuadrupedSkeleton::new())
|
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(
|
comp::Animation::Run => quadruped::RunAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
(vel.0.magnitude(), time),
|
(vel.0.magnitude(), time),
|
||||||
animation_history.time,
|
animation_info.time,
|
||||||
),
|
),
|
||||||
comp::Animation::Idle => quadruped::IdleAnimation::update_skeleton(
|
comp::Animation::Idle => quadruped::IdleAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
time,
|
time,
|
||||||
animation_history.time,
|
animation_info.time,
|
||||||
),
|
),
|
||||||
comp::Animation::Jump => quadruped::JumpAnimation::update_skeleton(
|
comp::Animation::Jump => quadruped::JumpAnimation::update_skeleton(
|
||||||
state.skeleton_mut(),
|
state.skeleton_mut(),
|
||||||
(vel.0.magnitude(), time),
|
(vel.0.magnitude(), time),
|
||||||
animation_history.time,
|
animation_info.time,
|
||||||
),
|
),
|
||||||
|
|
||||||
// TODO!
|
// TODO!
|
||||||
|
@ -9,6 +9,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use client::{self, Client, Input, InputEvent};
|
use client::{self, Client, Input, InputEvent};
|
||||||
use common::clock::Clock;
|
use common::clock::Clock;
|
||||||
|
use glutin::MouseButton;
|
||||||
use std::{cell::RefCell, mem, rc::Rc, time::Duration};
|
use std::{cell::RefCell, mem, rc::Rc, time::Duration};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -138,7 +139,11 @@ impl PlayState for SessionState {
|
|||||||
Event::Close => {
|
Event::Close => {
|
||||||
return PlayStateResult::Shutdown;
|
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::MoveForward) => self.key_state.up = true,
|
||||||
Event::KeyDown(Key::MoveBack) => self.key_state.down = true,
|
Event::KeyDown(Key::MoveBack) => self.key_state.down = true,
|
||||||
Event::KeyDown(Key::MoveLeft) => self.key_state.left = true,
|
Event::KeyDown(Key::MoveLeft) => self.key_state.left = true,
|
||||||
@ -148,7 +153,7 @@ impl PlayState for SessionState {
|
|||||||
self.key_state.jump = true;
|
self.key_state.jump = true;
|
||||||
}
|
}
|
||||||
Event::KeyDown(Key::Glide) => self.key_state.glide = 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::MoveForward) => self.key_state.up = false,
|
||||||
Event::KeyUp(Key::MoveBack) => self.key_state.down = false,
|
Event::KeyUp(Key::MoveBack) => self.key_state.down = false,
|
||||||
Event::KeyUp(Key::MoveLeft) => self.key_state.left = false,
|
Event::KeyUp(Key::MoveLeft) => self.key_state.left = false,
|
||||||
|
@ -3,6 +3,7 @@ use crate::{
|
|||||||
settings::Settings,
|
settings::Settings,
|
||||||
ui, Error,
|
ui, Error,
|
||||||
};
|
};
|
||||||
|
use glutin::{ElementState, MouseButton};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -124,7 +125,11 @@ impl Window {
|
|||||||
events.push(Event::Resize(Vec2::new(width as u32, height as u32)));
|
events.push(Event::Resize(Vec2::new(width as u32, height as u32)));
|
||||||
}
|
}
|
||||||
glutin::WindowEvent::ReceivedCharacter(c) => events.push(Event::Char(c)),
|
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
|
glutin::WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode
|
||||||
{
|
{
|
||||||
Some(keycode) => match key_map.get(&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.
|
/// A key has been typed that corresponds to a specific character.
|
||||||
Char(char),
|
Char(char),
|
||||||
/// The cursor has been panned across the screen while grabbed.
|
/// The cursor has been panned across the screen while grabbed.
|
||||||
|
Click(MouseButton, ElementState),
|
||||||
CursorPan(Vec2<f32>),
|
CursorPan(Vec2<f32>),
|
||||||
/// The camera has been requested to zoom.
|
/// The camera has been requested to zoom.
|
||||||
Zoom(f32),
|
Zoom(f32),
|
||||||
|
Reference in New Issue
Block a user