mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add better inputs, input validation and more
Former-commit-id: 3227221b12a674f66b011ce0ba734e226f223f34
This commit is contained in:
parent
3c0121538b
commit
4696cd2c8b
@ -70,9 +70,6 @@ impl Client {
|
|||||||
_ => return Err(Error::ServerWentMad),
|
_ => return Err(Error::ServerWentMad),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize ecs components the client has actions over
|
|
||||||
state.write_component(entity, comp::Inputs::default());
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
client_state,
|
client_state,
|
||||||
thread_pool: threadpool::Builder::new()
|
thread_pool: threadpool::Builder::new()
|
||||||
@ -94,11 +91,13 @@ impl Client {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request a state transition to `ClientState::Registered`.
|
||||||
pub fn register(&mut self, player: comp::Player) {
|
pub fn register(&mut self, player: comp::Player) {
|
||||||
self.postbox.send_message(ClientMsg::Register { player });
|
self.postbox.send_message(ClientMsg::Register { player });
|
||||||
self.client_state = ClientState::Pending;
|
self.client_state = ClientState::Pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Request a state transition to `ClientState::Character`.
|
||||||
pub fn request_character(&mut self, name: String, body: comp::Body) {
|
pub fn request_character(&mut self, name: String, body: comp::Body) {
|
||||||
self.postbox
|
self.postbox
|
||||||
.send_message(ClientMsg::Character { name, body });
|
.send_message(ClientMsg::Character { name, body });
|
||||||
@ -111,24 +110,56 @@ impl Client {
|
|||||||
.send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail
|
.send_message(ClientMsg::SetViewDistance(self.view_distance.unwrap())); // Can't fail
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the client's worker thread pool. This pool should be used for any
|
/// Send a chat message to the server.
|
||||||
/// computationally expensive operations that run outside of the main thread (i.e., threads that
|
|
||||||
/// block on I/O operations are exempt).
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn thread_pool(&self) -> &threadpool::ThreadPool {
|
pub fn send_chat(&mut self, msg: String) {
|
||||||
&self.thread_pool
|
self.postbox.send_message(ClientMsg::Chat(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the client's game state.
|
/// Jump locally, the new positions will be synced to the server
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn state(&self) -> &State {
|
pub fn jump(&mut self) {
|
||||||
&self.state
|
if self.client_state != ClientState::Character {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.state.write_component(self.entity, comp::Jumping);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to the client's game state.
|
/// Start to glide locally, animation will be synced
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn state_mut(&mut self) -> &mut State {
|
pub fn glide(&mut self, state: bool) {
|
||||||
&mut self.state
|
if self.client_state != ClientState::Character {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if state {
|
||||||
|
self.state.write_component(self.entity, comp::Gliding);
|
||||||
|
} else {
|
||||||
|
self.state
|
||||||
|
.ecs_mut()
|
||||||
|
.write_storage::<comp::Gliding>()
|
||||||
|
.remove(self.entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start to attack
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn attack(&mut self) {
|
||||||
|
if self.client_state != ClientState::Character {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: Test if attack is possible using timeout
|
||||||
|
self.state
|
||||||
|
.write_component(self.entity, comp::Attacking::start());
|
||||||
|
self.postbox.send_message(ClientMsg::Attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tell the server the client wants to respawn.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn respawn(&mut self) {
|
||||||
|
if self.client_state != ClientState::Dead {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.postbox.send_message(ClientMsg::Respawn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove all cached terrain
|
/// Remove all cached terrain
|
||||||
@ -138,38 +169,9 @@ impl Client {
|
|||||||
self.pending_chunks.clear();
|
self.pending_chunks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the player's entity.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn entity(&self) -> EcsEntity {
|
|
||||||
self.entity
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the client state
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn get_client_state(&self) -> ClientState {
|
|
||||||
self.client_state
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current tick number.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn get_tick(&self) -> u64 {
|
|
||||||
self.tick
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a chat message to the server.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn send_chat(&mut self, msg: String) {
|
|
||||||
self.postbox.send_message(ClientMsg::Chat(msg))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn get_ping_ms(&self) -> f64 {
|
|
||||||
self.last_ping_delta * 1000.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute a single client tick, handle input and update the game state by the given duration.
|
/// Execute a single client tick, handle input and update the game state by the given duration.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn tick(&mut self, input: comp::Inputs, dt: Duration) -> Result<Vec<Event>, Error> {
|
pub fn tick(&mut self, control: comp::Control, dt: Duration) -> Result<Vec<Event>, Error> {
|
||||||
// This tick function is the centre of the Veloren universe. Most client-side things are
|
// This tick function is the centre of the Veloren universe. Most client-side things are
|
||||||
// managed from here, and as such it's important that it stays organised. Please consult
|
// managed from here, and as such it's important that it stays organised. Please consult
|
||||||
// the core developers before making significant changes to this code. Here is the
|
// the core developers before making significant changes to this code. Here is the
|
||||||
@ -187,11 +189,9 @@ impl Client {
|
|||||||
// 1) Handle input from frontend.
|
// 1) Handle input from frontend.
|
||||||
// Pass character actions from frontend input to the player's entity.
|
// Pass character actions from frontend input to the player's entity.
|
||||||
// TODO: Only do this if the entity already has a Inputs component!
|
// TODO: Only do this if the entity already has a Inputs component!
|
||||||
self.state.write_component(self.entity, input.clone());
|
if self.client_state == ClientState::Character {
|
||||||
|
self.state.write_component(self.entity, control.clone());
|
||||||
// Tell the server about the inputs.
|
}
|
||||||
self.postbox
|
|
||||||
.send_message(ClientMsg::PlayerInputs(input.clone()));
|
|
||||||
|
|
||||||
// 2) Build up a list of events for this frame, to be passed to the frontend.
|
// 2) 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();
|
||||||
@ -288,6 +288,25 @@ impl Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 7) Finish the tick, pass control back to the frontend.
|
// 7) Finish the tick, pass control back to the frontend.
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
self.state
|
||||||
|
.ecs_mut()
|
||||||
|
.write_storage::<comp::Jumping>()
|
||||||
|
.remove(self.entity);
|
||||||
|
self.state
|
||||||
|
.ecs_mut()
|
||||||
|
.write_storage::<comp::Gliding>()
|
||||||
|
.remove(self.entity);
|
||||||
|
self.state
|
||||||
|
.ecs_mut()
|
||||||
|
.write_storage::<comp::Dying>()
|
||||||
|
.remove(self.entity);
|
||||||
|
self.state
|
||||||
|
.ecs_mut()
|
||||||
|
.write_storage::<comp::Respawning>()
|
||||||
|
.remove(self.entity);
|
||||||
|
|
||||||
self.tick += 1;
|
self.tick += 1;
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
@ -378,6 +397,49 @@ impl Client {
|
|||||||
|
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the player's entity.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn entity(&self) -> EcsEntity {
|
||||||
|
self.entity
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the client state
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_client_state(&self) -> ClientState {
|
||||||
|
self.client_state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current tick number.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_tick(&self) -> u64 {
|
||||||
|
self.tick
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn get_ping_ms(&self) -> f64 {
|
||||||
|
self.last_ping_delta * 1000.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the client's worker thread pool. This pool should be used for any
|
||||||
|
/// computationally expensive operations that run outside of the main thread (i.e., threads that
|
||||||
|
/// block on I/O operations are exempt).
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn thread_pool(&self) -> &threadpool::ThreadPool {
|
||||||
|
&self.thread_pool
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the client's game state.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn state(&self) -> &State {
|
||||||
|
&self.state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to the client's game state.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn state_mut(&mut self) -> &mut State {
|
||||||
|
&mut self.state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Client {
|
impl Drop for Client {
|
||||||
|
@ -1,33 +1,49 @@
|
|||||||
use specs::{Component, FlaggedStorage, VecStorage};
|
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum InputEvent {
|
pub struct Control {
|
||||||
Jump,
|
pub move_dir: Vec2<f32>,
|
||||||
Attack,
|
|
||||||
RequestRespawn,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct Respawning;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Inputs {
|
pub struct Attacking {
|
||||||
// Held down
|
pub time: f32,
|
||||||
pub move_dir: Vec2<f32>,
|
pub applied: bool,
|
||||||
pub jumping: bool,
|
|
||||||
pub gliding: bool,
|
|
||||||
|
|
||||||
// Event based
|
|
||||||
pub events: Vec<InputEvent>,
|
|
||||||
}
|
}
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Jumping;
|
||||||
|
|
||||||
impl Component for Inputs {
|
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Gliding;
|
||||||
|
|
||||||
|
impl Component for Control {
|
||||||
type Storage = VecStorage<Self>;
|
type Storage = VecStorage<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
impl Component for Respawning {
|
||||||
pub struct Actions {
|
type Storage = NullStorage<Self>;
|
||||||
pub attack_time: Option<f32>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Actions {
|
impl Attacking {
|
||||||
|
pub fn start() -> Self {
|
||||||
|
Self {
|
||||||
|
time: 0.0,
|
||||||
|
applied: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Component for Attacking {
|
||||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Component for Jumping {
|
||||||
|
type Storage = NullStorage<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Gliding {
|
||||||
|
type Storage = NullStorage<Self>;
|
||||||
|
}
|
||||||
|
@ -14,10 +14,11 @@ pub use actor::QuadrupedBody;
|
|||||||
pub use agent::Agent;
|
pub use agent::Agent;
|
||||||
pub use animation::Animation;
|
pub use animation::Animation;
|
||||||
pub use animation::AnimationInfo;
|
pub use animation::AnimationInfo;
|
||||||
pub use inputs::Actions;
|
pub use inputs::Attacking;
|
||||||
pub use inputs::InputEvent;
|
pub use inputs::Control;
|
||||||
pub use inputs::Inputs;
|
pub use inputs::Gliding;
|
||||||
|
pub use inputs::Jumping;
|
||||||
|
pub use inputs::Respawning;
|
||||||
pub use player::Player;
|
pub use player::Player;
|
||||||
pub use player::Respawn;
|
|
||||||
pub use stats::Dying;
|
pub use stats::Dying;
|
||||||
pub use stats::Stats;
|
pub use stats::Stats;
|
||||||
|
@ -21,6 +21,12 @@ pub struct Stats {
|
|||||||
pub xp: u32,
|
pub xp: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Stats {
|
||||||
|
pub fn is_dead(&self) -> bool {
|
||||||
|
self.hp.current == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Stats {
|
impl Default for Stats {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -11,12 +11,13 @@ pub enum ClientMsg {
|
|||||||
name: String,
|
name: String,
|
||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
},
|
},
|
||||||
|
Attack,
|
||||||
|
Respawn,
|
||||||
RequestState(ClientState),
|
RequestState(ClientState),
|
||||||
SetViewDistance(u32),
|
SetViewDistance(u32),
|
||||||
Ping,
|
Ping,
|
||||||
Pong,
|
Pong,
|
||||||
Chat(String),
|
Chat(String),
|
||||||
PlayerInputs(comp::Inputs),
|
|
||||||
PlayerAnimation(comp::AnimationInfo),
|
PlayerAnimation(comp::AnimationInfo),
|
||||||
PlayerPhysics {
|
PlayerPhysics {
|
||||||
pos: comp::phys::Pos,
|
pos: comp::phys::Pos,
|
||||||
|
@ -23,7 +23,7 @@ sphynx::sum_type! {
|
|||||||
Actor(comp::Actor),
|
Actor(comp::Actor),
|
||||||
Player(comp::Player),
|
Player(comp::Player),
|
||||||
Stats(comp::Stats),
|
Stats(comp::Stats),
|
||||||
Actions(comp::Actions),
|
Attacking(comp::Attacking),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Automatically derive From<T> for EcsCompPhantom
|
// Automatically derive From<T> for EcsCompPhantom
|
||||||
@ -37,7 +37,7 @@ sphynx::sum_type! {
|
|||||||
Actor(PhantomData<comp::Actor>),
|
Actor(PhantomData<comp::Actor>),
|
||||||
Player(PhantomData<comp::Player>),
|
Player(PhantomData<comp::Player>),
|
||||||
Stats(PhantomData<comp::Stats>),
|
Stats(PhantomData<comp::Stats>),
|
||||||
Actions(PhantomData<comp::Actions>),
|
Attacking(PhantomData<comp::Attacking>),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl sphynx::CompPacket for EcsCompPacket {
|
impl sphynx::CompPacket for EcsCompPacket {
|
||||||
|
@ -103,7 +103,7 @@ impl State {
|
|||||||
ecs.register_synced::<comp::Actor>();
|
ecs.register_synced::<comp::Actor>();
|
||||||
ecs.register_synced::<comp::Player>();
|
ecs.register_synced::<comp::Player>();
|
||||||
ecs.register_synced::<comp::Stats>();
|
ecs.register_synced::<comp::Stats>();
|
||||||
ecs.register_synced::<comp::Actions>();
|
ecs.register_synced::<comp::Attacking>();
|
||||||
ecs.register::<comp::phys::ForceUpdate>();
|
ecs.register::<comp::phys::ForceUpdate>();
|
||||||
|
|
||||||
// Register unsynced (or synced by other means) components.
|
// Register unsynced (or synced by other means) components.
|
||||||
@ -111,9 +111,12 @@ impl State {
|
|||||||
ecs.register::<comp::phys::Vel>();
|
ecs.register::<comp::phys::Vel>();
|
||||||
ecs.register::<comp::phys::Dir>();
|
ecs.register::<comp::phys::Dir>();
|
||||||
ecs.register::<comp::AnimationInfo>();
|
ecs.register::<comp::AnimationInfo>();
|
||||||
ecs.register::<comp::Inputs>();
|
ecs.register::<comp::Attacking>();
|
||||||
|
ecs.register::<comp::Control>();
|
||||||
|
ecs.register::<comp::Jumping>();
|
||||||
|
ecs.register::<comp::Respawning>();
|
||||||
|
ecs.register::<comp::Gliding>();
|
||||||
ecs.register::<comp::Dying>();
|
ecs.register::<comp::Dying>();
|
||||||
ecs.register::<comp::Respawn>();
|
|
||||||
ecs.register::<comp::Agent>();
|
ecs.register::<comp::Agent>();
|
||||||
ecs.register::<inventory::Inventory>();
|
ecs.register::<inventory::Inventory>();
|
||||||
|
|
||||||
@ -192,7 +195,8 @@ impl State {
|
|||||||
|
|
||||||
/// Removes every chunk of the terrain.
|
/// Removes every chunk of the terrain.
|
||||||
pub fn clear_terrain(&mut self) {
|
pub fn clear_terrain(&mut self) {
|
||||||
let keys = self.terrain_mut()
|
let keys = self
|
||||||
|
.terrain_mut()
|
||||||
.drain()
|
.drain()
|
||||||
.map(|(key, _)| key)
|
.map(|(key, _)| key)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -6,7 +6,7 @@ use vek::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
phys::{Dir, Pos, Vel},
|
phys::{Dir, Pos, Vel},
|
||||||
Actions, Animation, AnimationInfo,
|
Animation, AnimationInfo, Attacking,
|
||||||
},
|
},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
terrain::TerrainMap,
|
terrain::TerrainMap,
|
||||||
@ -17,18 +17,27 @@ use crate::{
|
|||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
|
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (Entities<'a>, Read<'a, DeltaTime>, WriteStorage<'a, Actions>);
|
type SystemData = (
|
||||||
|
Entities<'a>,
|
||||||
|
Read<'a, DeltaTime>,
|
||||||
|
WriteStorage<'a, Attacking>,
|
||||||
|
);
|
||||||
|
|
||||||
fn run(&mut self, (entities, dt, mut actions): Self::SystemData) {
|
fn run(&mut self, (entities, dt, mut attackings): Self::SystemData) {
|
||||||
for (entity, mut action) in (&entities, &mut actions).join() {
|
for (entity, attacking) in (&entities, &mut attackings).join() {
|
||||||
let should_end = action.attack_time.as_mut().map_or(false, |mut time| {
|
attacking.time += dt.0;
|
||||||
*time += dt.0;
|
|
||||||
*time > 0.5 // TODO: constant
|
|
||||||
});
|
|
||||||
|
|
||||||
if should_end {
|
|
||||||
action.attack_time = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let finished_attack = (&entities, &mut attackings)
|
||||||
|
.join()
|
||||||
|
.filter(|(e, a)| {
|
||||||
|
a.time > 0.5 // TODO: constant
|
||||||
|
})
|
||||||
|
.map(|(e, a)| e)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for entity in finished_attack {
|
||||||
|
attackings.remove(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,29 @@
|
|||||||
// Library
|
// Library
|
||||||
use specs::{Join, Read, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
// Crate
|
// Crate
|
||||||
use crate::comp::{phys::Pos, Agent, Inputs};
|
use crate::comp::{phys::Pos, Agent, Control, Jumping};
|
||||||
|
|
||||||
// Basic ECS AI agent system
|
// Basic ECS AI agent system
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
|
|
||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
|
Entities<'a>,
|
||||||
WriteStorage<'a, Agent>,
|
WriteStorage<'a, Agent>,
|
||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Inputs>,
|
WriteStorage<'a, Control>,
|
||||||
|
WriteStorage<'a, Jumping>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn run(&mut self, (mut agents, positions, mut inputs): Self::SystemData) {
|
fn run(
|
||||||
for (mut agent, pos, mut input) in (&mut agents, &positions, &mut inputs).join() {
|
&mut self,
|
||||||
|
(entities, mut agents, positions, mut controls, mut jumpings): Self::SystemData,
|
||||||
|
) {
|
||||||
|
for (entity, agent, pos, control) in
|
||||||
|
(&entities, &mut agents, &positions, &mut controls).join()
|
||||||
|
{
|
||||||
match agent {
|
match agent {
|
||||||
Agent::Wanderer(bearing) => {
|
Agent::Wanderer(bearing) => {
|
||||||
*bearing += Vec2::new(rand::random::<f32>() - 0.5, rand::random::<f32>() - 0.5)
|
*bearing += Vec2::new(rand::random::<f32>() - 0.5, rand::random::<f32>() - 0.5)
|
||||||
@ -25,7 +32,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
- pos.0 * 0.0002;
|
- pos.0 * 0.0002;
|
||||||
|
|
||||||
if bearing.magnitude_squared() != 0.0 {
|
if bearing.magnitude_squared() != 0.0 {
|
||||||
input.move_dir = bearing.normalized();
|
control.move_dir = bearing.normalized();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Agent::Pet { target, offset } => {
|
Agent::Pet { target, offset } => {
|
||||||
@ -34,12 +41,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
Some(tgt_pos) => {
|
Some(tgt_pos) => {
|
||||||
let tgt_pos = tgt_pos.0 + *offset;
|
let tgt_pos = tgt_pos.0 + *offset;
|
||||||
|
|
||||||
// Jump with target.
|
if tgt_pos.z > pos.0.z + 1.0 {
|
||||||
input.jumping = tgt_pos.z > pos.0.z + 1.0;
|
jumpings.insert(entity, Jumping);
|
||||||
|
}
|
||||||
|
|
||||||
// Move towards the target.
|
// Move towards the target.
|
||||||
let dist = tgt_pos.distance(pos.0);
|
let dist = tgt_pos.distance(pos.0);
|
||||||
input.move_dir = if dist > 5.0 {
|
control.move_dir = if dist > 5.0 {
|
||||||
Vec2::from(tgt_pos - pos.0).normalized()
|
Vec2::from(tgt_pos - pos.0).normalized()
|
||||||
} else if dist < 1.5 && dist > 0.0 {
|
} else if dist < 1.5 && dist > 0.0 {
|
||||||
Vec2::from(pos.0 - tgt_pos).normalized()
|
Vec2::from(pos.0 - tgt_pos).normalized()
|
||||||
@ -47,7 +55,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec2::zero()
|
Vec2::zero()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => input.move_dir = Vec2::zero(),
|
_ => control.move_dir = Vec2::zero(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change offset occasionally.
|
// Change offset occasionally.
|
||||||
|
@ -6,7 +6,7 @@ use vek::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
phys::{Dir, ForceUpdate, Pos, Vel},
|
phys::{Dir, ForceUpdate, Pos, Vel},
|
||||||
Actions, Animation, AnimationInfo, InputEvent, Inputs, Respawn, Stats,
|
Animation, AnimationInfo, Attacking, Control, Gliding, Jumping, Respawning, Stats,
|
||||||
},
|
},
|
||||||
state::{DeltaTime, Time},
|
state::{DeltaTime, Time},
|
||||||
terrain::TerrainMap,
|
terrain::TerrainMap,
|
||||||
@ -22,14 +22,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
Read<'a, Time>,
|
Read<'a, Time>,
|
||||||
Read<'a, DeltaTime>,
|
Read<'a, DeltaTime>,
|
||||||
ReadExpect<'a, TerrainMap>,
|
ReadExpect<'a, TerrainMap>,
|
||||||
WriteStorage<'a, Inputs>,
|
|
||||||
WriteStorage<'a, Actions>,
|
|
||||||
ReadStorage<'a, Pos>,
|
ReadStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Dir>,
|
WriteStorage<'a, Dir>,
|
||||||
WriteStorage<'a, AnimationInfo>,
|
WriteStorage<'a, AnimationInfo>,
|
||||||
WriteStorage<'a, Stats>,
|
WriteStorage<'a, Stats>,
|
||||||
WriteStorage<'a, Respawn>,
|
ReadStorage<'a, Control>,
|
||||||
|
ReadStorage<'a, Jumping>,
|
||||||
|
WriteStorage<'a, Respawning>,
|
||||||
|
WriteStorage<'a, Gliding>,
|
||||||
|
WriteStorage<'a, Attacking>,
|
||||||
WriteStorage<'a, ForceUpdate>,
|
WriteStorage<'a, ForceUpdate>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -40,27 +42,29 @@ impl<'a> System<'a> for Sys {
|
|||||||
time,
|
time,
|
||||||
dt,
|
dt,
|
||||||
terrain,
|
terrain,
|
||||||
mut inputs,
|
|
||||||
mut actions,
|
|
||||||
positions,
|
positions,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut directions,
|
mut directions,
|
||||||
mut animation_infos,
|
mut animation_infos,
|
||||||
mut stats,
|
mut stats,
|
||||||
mut respawns,
|
mut controls,
|
||||||
|
mut jumpings,
|
||||||
|
mut respawnings,
|
||||||
|
mut glidings,
|
||||||
|
mut attackings,
|
||||||
mut force_updates,
|
mut force_updates,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
for (entity, inputs, pos, mut dir, mut vel) in (
|
for (entity, pos, control, mut dir, mut vel) in (
|
||||||
&entities,
|
&entities,
|
||||||
&mut inputs,
|
|
||||||
&positions,
|
&positions,
|
||||||
|
&controls,
|
||||||
&mut directions,
|
&mut directions,
|
||||||
&mut velocities,
|
&mut velocities,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
// Handle held-down inputs
|
// Handle held-down control
|
||||||
let on_ground = terrain
|
let on_ground = terrain
|
||||||
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
|
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
|
||||||
.map(|vox| !vox.is_empty())
|
.map(|vox| !vox.is_empty())
|
||||||
@ -70,9 +74,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
let (gliding, friction) = if on_ground {
|
let (gliding, friction) = if on_ground {
|
||||||
// TODO: Don't hard-code this.
|
// TODO: Don't hard-code this.
|
||||||
// Apply physics to the player: acceleration and non-linear deceleration.
|
// Apply physics to the player: acceleration and non-linear deceleration.
|
||||||
vel.0 += Vec2::broadcast(dt.0) * inputs.move_dir * 200.0;
|
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 200.0;
|
||||||
|
|
||||||
if inputs.jumping {
|
if jumpings.get(entity).is_some() {
|
||||||
vel.0.z += 16.0;
|
vel.0.z += 16.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,9 +84,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
} else {
|
} else {
|
||||||
// TODO: Don't hard-code this.
|
// TODO: Don't hard-code this.
|
||||||
// Apply physics to the player: acceleration and non-linear deceleration.
|
// Apply physics to the player: acceleration and non-linear deceleration.
|
||||||
vel.0 += Vec2::broadcast(dt.0) * inputs.move_dir * 10.0;
|
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 10.0;
|
||||||
|
|
||||||
if inputs.gliding && vel.0.z < 0.0 {
|
if glidings.get(entity).is_some() && vel.0.z < 0.0 {
|
||||||
// TODO: Don't hard-code this.
|
// TODO: Don't hard-code this.
|
||||||
let anti_grav = 9.81 * 3.95 + vel.0.z.powf(2.0) * 0.2;
|
let anti_grav = 9.81 * 3.95 + vel.0.z.powf(2.0) * 0.2;
|
||||||
vel.0.z +=
|
vel.0.z +=
|
||||||
@ -109,12 +113,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let animation = if on_ground {
|
let animation = if on_ground {
|
||||||
if inputs.move_dir.magnitude() > 0.01 {
|
if control.move_dir.magnitude() > 0.01 {
|
||||||
Animation::Run
|
Animation::Run
|
||||||
} else {
|
} else {
|
||||||
Animation::Idle
|
Animation::Idle
|
||||||
}
|
}
|
||||||
} else if gliding {
|
} else if glidings.get(entity).is_some() {
|
||||||
Animation::Gliding
|
Animation::Gliding
|
||||||
} else {
|
} else {
|
||||||
Animation::Jump
|
Animation::Jump
|
||||||
@ -135,17 +139,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (entity, inputs) in (&entities, &mut inputs).join() {
|
|
||||||
// Handle event-based inputs
|
for (entity, pos, dir, attacking) in
|
||||||
for event in inputs.events.drain(..) {
|
(&entities, &positions, &directions, &mut attackings).join()
|
||||||
match event {
|
{
|
||||||
InputEvent::Attack => {
|
if !attacking.applied {
|
||||||
// Attack delay
|
|
||||||
if let (Some(pos), Some(dir), Some(action)) = (
|
|
||||||
positions.get(entity),
|
|
||||||
directions.get(entity),
|
|
||||||
actions.get_mut(entity),
|
|
||||||
) {
|
|
||||||
for (b, pos_b, mut stat_b, mut vel_b) in
|
for (b, pos_b, mut stat_b, mut vel_b) in
|
||||||
(&entities, &positions, &mut stats, &mut velocities).join()
|
(&entities, &positions, &mut stats, &mut velocities).join()
|
||||||
{
|
{
|
||||||
@ -154,23 +152,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
&& pos.0.distance_squared(pos_b.0) < 50.0
|
&& pos.0.distance_squared(pos_b.0) < 50.0
|
||||||
&& dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
|
&& dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
|
||||||
{
|
{
|
||||||
// Set action
|
|
||||||
action.attack_time = Some(0.0);
|
|
||||||
|
|
||||||
// Deal damage
|
// Deal damage
|
||||||
stat_b.hp.change_by(-10); // TODO: variable damage
|
stat_b.hp.change_by(-10); // TODO: variable damage
|
||||||
vel_b.0 += (pos_b.0 - pos.0).normalized() * 20.0;
|
vel_b.0 += (pos_b.0 - pos.0).normalized() * 10.0;
|
||||||
vel_b.0.z = 20.0;
|
vel_b.0.z = 15.0;
|
||||||
force_updates.insert(b, ForceUpdate);
|
force_updates.insert(b, ForceUpdate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
attacking.applied = true;
|
||||||
}
|
|
||||||
InputEvent::RequestRespawn => {
|
|
||||||
respawns.insert(entity, Respawn);
|
|
||||||
}
|
|
||||||
InputEvent::Jump => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,11 @@ use crate::{
|
|||||||
dyna::{Dyna, DynaErr},
|
dyna::{Dyna, DynaErr},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{collections::{hash_map, HashMap}, marker::PhantomData, sync::Arc};
|
use std::{
|
||||||
|
collections::{hash_map, HashMap},
|
||||||
|
marker::PhantomData,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -141,7 +141,10 @@ fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
|||||||
fn handle_kill(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
fn handle_kill(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
server
|
server
|
||||||
.state
|
.state
|
||||||
.write_component::<comp::Dying>(entity, comp::Dying)
|
.ecs_mut()
|
||||||
|
.write_storage::<comp::Stats>()
|
||||||
|
.get_mut(entity)
|
||||||
|
.map(|s| s.hp.current = 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||||
@ -208,9 +211,11 @@ fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &Cha
|
|||||||
.state
|
.state
|
||||||
.read_component_cloned::<comp::phys::Pos>(entity)
|
.read_component_cloned::<comp::phys::Pos>(entity)
|
||||||
{
|
{
|
||||||
Some(pos) => {
|
Some(mut pos) => {
|
||||||
|
pos.0.x += 1.0;
|
||||||
server
|
server
|
||||||
.create_npc(
|
.create_npc(
|
||||||
|
pos,
|
||||||
"Bungo".to_owned(),
|
"Bungo".to_owned(),
|
||||||
comp::Body::Quadruped(comp::QuadrupedBody::random()),
|
comp::Body::Quadruped(comp::QuadrupedBody::random()),
|
||||||
)
|
)
|
||||||
@ -218,7 +223,6 @@ fn handle_pet(server: &mut Server, entity: EcsEntity, args: String, action: &Cha
|
|||||||
target: entity,
|
target: entity,
|
||||||
offset: Vec2::zero(),
|
offset: Vec2::zero(),
|
||||||
})
|
})
|
||||||
.with(pos)
|
|
||||||
.build();
|
.build();
|
||||||
server
|
server
|
||||||
.clients
|
.clients
|
||||||
|
@ -126,15 +126,19 @@ impl Server {
|
|||||||
|
|
||||||
/// Build a non-player character.
|
/// Build a non-player character.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn create_npc(&mut self, name: String, body: comp::Body) -> EcsEntityBuilder {
|
pub fn create_npc(
|
||||||
|
&mut self,
|
||||||
|
pos: comp::phys::Pos,
|
||||||
|
name: String,
|
||||||
|
body: comp::Body,
|
||||||
|
) -> EcsEntityBuilder {
|
||||||
self.state
|
self.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(comp::phys::Pos(Vec3::new(0.0, 0.0, 64.0)))
|
.with(pos)
|
||||||
|
.with(comp::Control::default())
|
||||||
.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::Inputs::default())
|
|
||||||
.with(comp::Actions::default())
|
|
||||||
.with(comp::Actor::Character { name, body })
|
.with(comp::Actor::Character { name, body })
|
||||||
.with(comp::Stats::default())
|
.with(comp::Stats::default())
|
||||||
}
|
}
|
||||||
@ -150,8 +154,7 @@ impl Server {
|
|||||||
|
|
||||||
state.write_component(entity, comp::Actor::Character { name, body });
|
state.write_component(entity, comp::Actor::Character { name, body });
|
||||||
state.write_component(entity, comp::Stats::default());
|
state.write_component(entity, comp::Stats::default());
|
||||||
state.write_component(entity, comp::Inputs::default());
|
state.write_component(entity, comp::Control::default());
|
||||||
state.write_component(entity, comp::Actions::default());
|
|
||||||
state.write_component(entity, comp::AnimationInfo::new());
|
state.write_component(entity, comp::AnimationInfo::new());
|
||||||
state.write_component(entity, comp::phys::Pos(spawn_point));
|
state.write_component(entity, comp::phys::Pos(spawn_point));
|
||||||
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
|
||||||
@ -210,25 +213,17 @@ impl Server {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for entity in todo_kill {
|
for entity in todo_kill {
|
||||||
self.state
|
|
||||||
.ecs_mut()
|
|
||||||
.write_storage::<comp::Dying>()
|
|
||||||
.remove(entity);
|
|
||||||
self.state
|
|
||||||
.ecs_mut()
|
|
||||||
.write_storage::<comp::Actions>()
|
|
||||||
.remove(entity);
|
|
||||||
if let Some(client) = self.clients.get_mut(&entity) {
|
if let Some(client) = self.clients.get_mut(&entity) {
|
||||||
client.force_state(ClientState::Dead);
|
client.force_state(ClientState::Dead);
|
||||||
} else {
|
} else {
|
||||||
//self.state.ecs_mut().delete_entity_synced(entity);
|
self.state.ecs_mut().delete_entity_synced(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle respawns
|
// Handle respawns
|
||||||
let todo_respawn = (
|
let todo_respawn = (
|
||||||
&self.state.ecs().entities(),
|
&self.state.ecs().entities(),
|
||||||
&self.state.ecs().read_storage::<comp::Respawn>(),
|
&self.state.ecs().read_storage::<comp::Respawning>(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.map(|(entity, _)| entity)
|
.map(|(entity, _)| entity)
|
||||||
@ -237,12 +232,7 @@ impl Server {
|
|||||||
for entity in todo_respawn {
|
for entity in todo_respawn {
|
||||||
if let Some(client) = self.clients.get_mut(&entity) {
|
if let Some(client) = self.clients.get_mut(&entity) {
|
||||||
client.allow_state(ClientState::Character);
|
client.allow_state(ClientState::Character);
|
||||||
self.state
|
|
||||||
.ecs_mut()
|
|
||||||
.write_storage::<comp::Respawn>()
|
|
||||||
.remove(entity);
|
|
||||||
self.state.write_component(entity, comp::Stats::default());
|
self.state.write_component(entity, comp::Stats::default());
|
||||||
self.state.write_component(entity, comp::Actions::default());
|
|
||||||
self.state
|
self.state
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.write_storage::<comp::phys::Pos>()
|
.write_storage::<comp::phys::Pos>()
|
||||||
@ -319,6 +309,16 @@ impl Server {
|
|||||||
self.sync_clients();
|
self.sync_clients();
|
||||||
|
|
||||||
// 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::Jumping>().remove(entity);
|
||||||
|
ecs.write_storage::<comp::Gliding>().remove(entity);
|
||||||
|
ecs.write_storage::<comp::Dying>().remove(entity);
|
||||||
|
ecs.write_storage::<comp::Respawning>().remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(frontend_events)
|
Ok(frontend_events)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,6 +442,18 @@ impl Server {
|
|||||||
}
|
}
|
||||||
ClientState::Pending => {}
|
ClientState::Pending => {}
|
||||||
},
|
},
|
||||||
|
ClientMsg::Attack => match client.client_state {
|
||||||
|
ClientState::Character => {
|
||||||
|
state.write_component(entity, comp::Attacking::start());
|
||||||
|
}
|
||||||
|
_ => client.error_state(RequestStateError::Impossible),
|
||||||
|
},
|
||||||
|
ClientMsg::Respawn => match client.client_state {
|
||||||
|
ClientState::Dead => {
|
||||||
|
state.write_component(entity, comp::Respawning);
|
||||||
|
}
|
||||||
|
_ => client.error_state(RequestStateError::Impossible),
|
||||||
|
},
|
||||||
ClientMsg::Chat(msg) => match client.client_state {
|
ClientMsg::Chat(msg) => match client.client_state {
|
||||||
ClientState::Connected => {
|
ClientState::Connected => {
|
||||||
client.error_state(RequestStateError::Impossible)
|
client.error_state(RequestStateError::Impossible)
|
||||||
@ -452,18 +464,6 @@ impl Server {
|
|||||||
| ClientState::Character => new_chat_msgs.push((entity, msg)),
|
| ClientState::Character => new_chat_msgs.push((entity, msg)),
|
||||||
ClientState::Pending => {}
|
ClientState::Pending => {}
|
||||||
},
|
},
|
||||||
ClientMsg::PlayerInputs(mut inputs) => match client.client_state {
|
|
||||||
ClientState::Character | ClientState::Dead => {
|
|
||||||
state
|
|
||||||
.ecs_mut()
|
|
||||||
.write_storage::<comp::Inputs>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|s| {
|
|
||||||
s.events.append(&mut inputs.events);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => client.error_state(RequestStateError::Impossible),
|
|
||||||
},
|
|
||||||
ClientMsg::PlayerAnimation(animation_info) => {
|
ClientMsg::PlayerAnimation(animation_info) => {
|
||||||
match client.client_state {
|
match client.client_state {
|
||||||
ClientState::Character => {
|
ClientState::Character => {
|
||||||
|
@ -5,8 +5,6 @@ pub struct KeyState {
|
|||||||
pub left: bool,
|
pub left: bool,
|
||||||
pub up: bool,
|
pub up: bool,
|
||||||
pub down: bool,
|
pub down: bool,
|
||||||
pub jump: bool,
|
|
||||||
pub glide: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeyState {
|
impl KeyState {
|
||||||
@ -16,8 +14,6 @@ impl KeyState {
|
|||||||
left: false,
|
left: false,
|
||||||
up: false,
|
up: false,
|
||||||
down: false,
|
down: false,
|
||||||
jump: false,
|
|
||||||
glide: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ impl PlayState for CharSelectionState {
|
|||||||
// Tick the client (currently only to keep the connection alive).
|
// Tick the client (currently only to keep the connection alive).
|
||||||
self.client
|
self.client
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.tick(comp::Inputs::default(), clock.get_last_delta())
|
.tick(comp::Control::default(), clock.get_last_delta())
|
||||||
.expect("Failed to tick the client");
|
.expect("Failed to tick the client");
|
||||||
self.client.borrow_mut().cleanup();
|
self.client.borrow_mut().cleanup();
|
||||||
|
|
||||||
|
@ -366,7 +366,8 @@ impl FigureMgr {
|
|||||||
.and_then(|stats| stats.hp.last_change)
|
.and_then(|stats| stats.hp.last_change)
|
||||||
.map(|(change_by, time)| {
|
.map(|(change_by, time)| {
|
||||||
Rgba::broadcast(1.0)
|
Rgba::broadcast(1.0)
|
||||||
+ Rgba::new(0.0, -1.0, -1.0, 0.0).map(|c| (c / (1.0 + DAMAGE_FADE_COEFFICIENT * time)) as f32)
|
+ Rgba::new(0.0, -1.0, -1.0, 0.0)
|
||||||
|
.map(|c| (c / (1.0 + DAMAGE_FADE_COEFFICIENT * time)) as f32)
|
||||||
})
|
})
|
||||||
.unwrap_or(Rgba::broadcast(1.0));
|
.unwrap_or(Rgba::broadcast(1.0));
|
||||||
|
|
||||||
@ -460,13 +461,17 @@ impl FigureMgr {
|
|||||||
let tick = client.get_tick();
|
let tick = client.get_tick();
|
||||||
let ecs = client.state().ecs();
|
let ecs = client.state().ecs();
|
||||||
|
|
||||||
for (entity, actor, _) in (
|
for (entity, actor, stats) in (
|
||||||
&ecs.entities(),
|
&ecs.entities(),
|
||||||
&ecs.read_storage::<comp::Actor>(),
|
&ecs.read_storage::<comp::Actor>(),
|
||||||
&ecs.read_storage::<comp::Actions>(),
|
&ecs.read_storage::<comp::Stats>(), // Just to make sure the entity is alive
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
|
if stats.is_dead() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
match actor {
|
match actor {
|
||||||
comp::Actor::Character { body, .. } => {
|
comp::Actor::Character { body, .. } => {
|
||||||
if let Some((locals, bone_consts)) = match body {
|
if let Some((locals, bone_consts)) = match body {
|
||||||
|
@ -19,7 +19,6 @@ pub struct SessionState {
|
|||||||
scene: Scene,
|
scene: Scene,
|
||||||
client: Rc<RefCell<Client>>,
|
client: Rc<RefCell<Client>>,
|
||||||
key_state: KeyState,
|
key_state: KeyState,
|
||||||
input_events: Vec<comp::InputEvent>,
|
|
||||||
hud: Hud,
|
hud: Hud,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +33,6 @@ impl SessionState {
|
|||||||
client,
|
client,
|
||||||
key_state: KeyState::new(),
|
key_state: KeyState::new(),
|
||||||
hud: Hud::new(window),
|
hud: Hud::new(window),
|
||||||
input_events: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,19 +58,11 @@ impl SessionState {
|
|||||||
let dir_vec = self.key_state.dir_vec();
|
let dir_vec = self.key_state.dir_vec();
|
||||||
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
|
||||||
|
|
||||||
// Take the input events.
|
for event in self
|
||||||
let mut input_events = Vec::new();
|
.client
|
||||||
mem::swap(&mut self.input_events, &mut input_events);
|
.borrow_mut()
|
||||||
|
.tick(comp::Control { move_dir }, dt)?
|
||||||
for event in self.client.borrow_mut().tick(
|
{
|
||||||
comp::Inputs {
|
|
||||||
move_dir,
|
|
||||||
jumping: self.key_state.jump,
|
|
||||||
gliding: self.key_state.glide,
|
|
||||||
events: input_events,
|
|
||||||
},
|
|
||||||
dt,
|
|
||||||
)? {
|
|
||||||
match event {
|
match event {
|
||||||
client::Event::Chat(msg) => {
|
client::Event::Chat(msg) => {
|
||||||
self.hud.new_message(msg);
|
self.hud.new_message(msg);
|
||||||
@ -136,21 +126,19 @@ impl PlayState for SessionState {
|
|||||||
return PlayStateResult::Shutdown;
|
return PlayStateResult::Shutdown;
|
||||||
}
|
}
|
||||||
// Attack key pressed
|
// Attack key pressed
|
||||||
Event::InputUpdate(GameInput::Attack, state) => {
|
Event::InputUpdate(GameInput::Attack, true) => {
|
||||||
self.input_events.push(comp::InputEvent::Attack);
|
self.client.borrow_mut().attack();
|
||||||
//self.input_events.push(comp::InputEvent::RequestRespawn);
|
self.client.borrow_mut().respawn();
|
||||||
},
|
}
|
||||||
|
Event::InputUpdate(GameInput::Jump, true) => {
|
||||||
|
self.client.borrow_mut().jump();
|
||||||
|
}
|
||||||
Event::InputUpdate(GameInput::MoveForward, state) => self.key_state.up = state,
|
Event::InputUpdate(GameInput::MoveForward, state) => self.key_state.up = state,
|
||||||
Event::InputUpdate(GameInput::MoveBack, state) => self.key_state.down = state,
|
Event::InputUpdate(GameInput::MoveBack, state) => self.key_state.down = state,
|
||||||
Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state,
|
Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state,
|
||||||
Event::InputUpdate(GameInput::MoveRight, state) => self.key_state.right = state,
|
Event::InputUpdate(GameInput::MoveRight, state) => self.key_state.right = state,
|
||||||
Event::InputUpdate(GameInput::Glide, state) => self.key_state.glide = state,
|
Event::InputUpdate(GameInput::Glide, state) => {
|
||||||
Event::InputUpdate(GameInput::Jump, true) => {
|
self.client.borrow_mut().glide(state)
|
||||||
self.input_events.push(comp::InputEvent::Jump);
|
|
||||||
self.key_state.jump = true;
|
|
||||||
}
|
|
||||||
Event::InputUpdate(GameInput::Jump, false) => {
|
|
||||||
self.key_state.jump = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass all other events to the scene
|
// Pass all other events to the scene
|
||||||
|
@ -113,12 +113,18 @@ impl Window {
|
|||||||
key_map.insert(settings.controls.map, GameInput::Map);
|
key_map.insert(settings.controls.map, GameInput::Map);
|
||||||
key_map.insert(settings.controls.bag, GameInput::Bag);
|
key_map.insert(settings.controls.bag, GameInput::Bag);
|
||||||
key_map.insert(settings.controls.quest_log, GameInput::QuestLog);
|
key_map.insert(settings.controls.quest_log, GameInput::QuestLog);
|
||||||
key_map.insert(settings.controls.character_window, GameInput::CharacterWindow);
|
key_map.insert(
|
||||||
|
settings.controls.character_window,
|
||||||
|
GameInput::CharacterWindow,
|
||||||
|
);
|
||||||
key_map.insert(settings.controls.social, GameInput::Social);
|
key_map.insert(settings.controls.social, GameInput::Social);
|
||||||
key_map.insert(settings.controls.spellbook, GameInput::Spellbook);
|
key_map.insert(settings.controls.spellbook, GameInput::Spellbook);
|
||||||
key_map.insert(settings.controls.settings, GameInput::Settings);
|
key_map.insert(settings.controls.settings, GameInput::Settings);
|
||||||
key_map.insert(settings.controls.help, GameInput::Help);
|
key_map.insert(settings.controls.help, GameInput::Help);
|
||||||
key_map.insert(settings.controls.toggle_interface, GameInput::ToggleInterface);
|
key_map.insert(
|
||||||
|
settings.controls.toggle_interface,
|
||||||
|
GameInput::ToggleInterface,
|
||||||
|
);
|
||||||
key_map.insert(settings.controls.toggle_debug, GameInput::ToggleDebug);
|
key_map.insert(settings.controls.toggle_debug, GameInput::ToggleDebug);
|
||||||
key_map.insert(settings.controls.fullscreen, GameInput::Fullscreen);
|
key_map.insert(settings.controls.fullscreen, GameInput::Fullscreen);
|
||||||
key_map.insert(settings.controls.screenshot, GameInput::Screenshot);
|
key_map.insert(settings.controls.screenshot, GameInput::Screenshot);
|
||||||
@ -184,11 +190,12 @@ 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, .. }
|
glutin::WindowEvent::MouseInput { button, state, .. } if cursor_grabbed => {
|
||||||
if cursor_grabbed =>
|
|
||||||
{
|
|
||||||
if let Some(&game_input) = key_map.get(&KeyMouse::Mouse(button)) {
|
if let Some(&game_input) = key_map.get(&KeyMouse::Mouse(button)) {
|
||||||
events.push(Event::InputUpdate(game_input, state == glutin::ElementState::Pressed))
|
events.push(Event::InputUpdate(
|
||||||
|
game_input,
|
||||||
|
state == glutin::ElementState::Pressed,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glutin::WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode
|
glutin::WindowEvent::KeyboardInput { input, .. } => match input.virtual_keycode
|
||||||
@ -204,7 +211,10 @@ impl Window {
|
|||||||
glutin::ElementState::Pressed => take_screenshot = true,
|
glutin::ElementState::Pressed => take_screenshot = true,
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Some(&game_input) => events.push(Event::InputUpdate(game_input, input.state == glutin::ElementState::Pressed)),
|
Some(&game_input) => events.push(Event::InputUpdate(
|
||||||
|
game_input,
|
||||||
|
input.state == glutin::ElementState::Pressed,
|
||||||
|
)),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
|
Loading…
Reference in New Issue
Block a user