Merge branch 'inputs-enhancements' into 'master'

Inputs enhancements

See merge request veloren/veloren!671
This commit is contained in:
Marcel 2019-11-29 15:20:35 +00:00
commit a6ef802f9c
8 changed files with 181 additions and 52 deletions

View File

@ -1,8 +1,13 @@
use specs::{Component, FlaggedStorage}; use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage; use specs_idvs::IDVStorage;
use sphynx::Uid; use sphynx::Uid;
use std::ops::Add;
use std::time::Duration;
use vek::*; use vek::*;
/// Default duration for how long before an input is considered 'held'.
pub const DEFAULT_HOLD_DURATION: Duration = Duration::from_millis(250);
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ControlEvent { pub enum ControlEvent {
Mount(Uid), Mount(Uid),
@ -11,18 +16,101 @@ pub enum ControlEvent {
//Respawn, //Respawn,
} }
/// The various states an input can be in
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum InputState {
Pressed,
Unpressed,
}
/// Whether a key is pressed or unpressed
/// and how long it has been in that state
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Input {
// Should not be pub because duration should
// always be reset when state is updated
state: InputState,
// Should only be updated by npc agents
// through appropriate fn
duration: Duration,
}
impl Input {
/// Whether input is in `InputState::Pressed` state
pub fn is_pressed(&self) -> bool {
self.state == InputState::Pressed
}
/// Whether input has been in current state longer than
/// `DEFAULT_HOLD_DURATION`
pub fn is_held_down(&self) -> bool {
(self.is_pressed() && self.duration >= DEFAULT_HOLD_DURATION)
}
/// Sets the `input::state` and resets `input::duration`
///
///
/// `new_state` == `true` -> `InputState::Pressed`
///
/// `new_state` == `false` -> `InputState::Unpressed`
pub fn set_state(&mut self, new_state: bool) {
// Only update if state switches
match (new_state, self.is_pressed()) {
(true, false) => {
self.state = InputState::Pressed;
self.duration = Duration::default();
}
(false, true) => {
self.state = InputState::Unpressed;
self.duration = Duration::default();
}
(_, _) => {}
};
}
/// Sets `input::duration`
pub fn inc_dur(&mut self, dur: Duration) {
self.duration = self.duration + dur;
}
/// Returns `input::duration`
pub fn get_dur(&self) -> Duration {
self.duration
}
}
impl Default for Input {
fn default() -> Self {
Self {
state: InputState::Unpressed,
duration: Duration::default(),
}
}
}
impl Add<Duration> for Input {
type Output = Self;
fn add(self, dur: Duration) -> Self {
Self {
state: self.state,
duration: self.duration.checked_add(dur).unwrap_or_default(),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct ControllerInputs { pub struct ControllerInputs {
pub primary: bool, pub primary: Input,
pub secondary: bool, pub secondary: Input,
pub sit: bool, pub sit: Input,
pub jump: bool, pub jump: Input,
pub roll: bool, pub roll: Input,
pub glide: bool, pub glide: Input,
pub climb: bool, pub climb: Input,
pub climb_down: bool, pub climb_down: Input,
pub wall_leap: bool, pub wall_leap: Input,
pub respawn: bool, pub respawn: Input,
pub toggle_wield: Input,
pub move_dir: Vec2<f32>, pub move_dir: Vec2<f32>,
pub look_dir: Vec3<f32>, pub look_dir: Vec3<f32>,
} }
@ -34,7 +122,25 @@ pub struct Controller {
pub events: Vec<ControlEvent>, pub events: Vec<ControlEvent>,
} }
impl ControllerInputs {
/// Updates all inputs, accounting for delta time
pub fn tick(&mut self, dt: Duration) {
self.primary = self.primary + dt;
self.secondary = self.secondary + dt;
self.sit = self.sit + dt;
self.jump = self.jump + dt;
self.roll = self.roll + dt;
self.glide = self.glide + dt;
self.climb = self.climb + dt;
self.climb_down = self.climb_down + dt;
self.wall_leap = self.wall_leap + dt;
self.respawn = self.respawn + dt;
self.toggle_wield = self.toggle_wield + dt;
}
}
impl Controller { impl Controller {
/// Sets all inputs to default
pub fn reset(&mut self) { pub fn reset(&mut self) {
*self = Self::default(); *self = Self::default();
} }

View File

@ -22,7 +22,8 @@ pub use body::{
}; };
pub use character_state::{ActionState, CharacterState, MovementState}; pub use character_state::{ActionState, CharacterState, MovementState};
pub use controller::{ pub use controller::{
ControlEvent, Controller, ControllerInputs, InventoryManip, MountState, Mounting, ControlEvent, Controller, ControllerInputs, Input, InputState, InventoryManip, MountState,
Mounting,
}; };
pub use inputs::CanBuild; pub use inputs::CanBuild;
pub use inventory::{item, Inventory, InventoryUpdate, Item, ItemKind}; pub use inventory::{item, Inventory, InventoryUpdate, Item, ItemKind};

View File

@ -67,7 +67,7 @@ impl<'a> System<'a> for Sys {
let tgt_pos = tgt_pos.0 + *offset; let tgt_pos = tgt_pos.0 + *offset;
if tgt_pos.z > pos.0.z + 1.0 { if tgt_pos.z > pos.0.z + 1.0 {
inputs.jump = true; inputs.jump.set_state(true);
} }
// Move towards the target. // Move towards the target.
@ -113,19 +113,19 @@ impl<'a> System<'a> for Sys {
// Fight (and slowly move closer) // Fight (and slowly move closer)
inputs.move_dir = inputs.move_dir =
Vec2::<f32>::from(target_pos.0 - pos.0).normalized() * 0.01; Vec2::<f32>::from(target_pos.0 - pos.0).normalized() * 0.01;
inputs.primary = true; inputs.primary.set_state(true);
} else if dist < SIGHT_DIST { } else if dist < SIGHT_DIST {
inputs.move_dir = inputs.move_dir =
Vec2::<f32>::from(target_pos.0 - pos.0).normalized() * 0.96; Vec2::<f32>::from(target_pos.0 - pos.0).normalized() * 0.96;
if rand::random::<f32>() < 0.02 { if rand::random::<f32>() < 0.02 {
inputs.roll = true; inputs.roll.set_state(true);
} }
if target_character.movement == Glide && target_pos.0.z > pos.0.z + 5.0 if target_character.movement == Glide && target_pos.0.z > pos.0.z + 5.0
{ {
inputs.glide = true; inputs.glide.set_state(true);
inputs.jump = true; inputs.jump.set_state(true);
} }
} else { } else {
choose_new = true; choose_new = true;

View File

@ -70,7 +70,7 @@ impl<'a> System<'a> for Sys {
if stats.is_dead { if stats.is_dead {
// Respawn // Respawn
if inputs.respawn { if inputs.respawn.is_pressed() {
server_emitter.emit(ServerEvent::Respawn(entity)); server_emitter.emit(ServerEvent::Respawn(entity));
} }
continue; continue;
@ -97,19 +97,19 @@ impl<'a> System<'a> for Sys {
// Glide // Glide
// TODO: Check for glide ability/item // TODO: Check for glide ability/item
if inputs.glide if inputs.glide.is_pressed()
&& !physics.on_ground && !physics.on_ground
&& (character.action == Idle || character.action.is_wield()) && (character.action == Idle || character.action.is_wield())
&& character.movement == Jump && character.movement == Jump
&& body.is_humanoid() && body.is_humanoid()
{ {
character.movement = Glide; character.movement = Glide;
} else if !inputs.glide && character.movement == Glide { } else if !inputs.glide.is_pressed() && character.movement == Glide {
character.movement = Jump; // character.movement = Jump;
} }
// Sit // Sit
if inputs.sit if inputs.sit.is_pressed()
&& physics.on_ground && physics.on_ground
&& character.action == Idle && character.action == Idle
&& character.movement != Sit && character.movement != Sit
@ -123,7 +123,7 @@ impl<'a> System<'a> for Sys {
} }
// Wield // Wield
if inputs.primary if inputs.primary.is_pressed()
&& character.action == Idle && character.action == Idle
&& (character.movement == Stand || character.movement == Run) && (character.movement == Stand || character.movement == Run)
{ {
@ -138,7 +138,7 @@ impl<'a> System<'a> for Sys {
power, power,
.. ..
}) => { }) => {
if inputs.primary if inputs.primary.is_pressed()
&& (character.movement == Stand && (character.movement == Stand
|| character.movement == Run || character.movement == Run
|| character.movement == Jump) || character.movement == Jump)
@ -177,7 +177,7 @@ impl<'a> System<'a> for Sys {
.. ..
}) => { }) => {
// Melee Attack // Melee Attack
if inputs.primary if inputs.primary.is_pressed()
&& (character.movement == Stand && (character.movement == Stand
|| character.movement == Run || character.movement == Run
|| character.movement == Jump) || character.movement == Jump)
@ -192,7 +192,7 @@ impl<'a> System<'a> for Sys {
} }
} }
// Magical Bolt // Magical Bolt
if inputs.secondary if inputs.secondary.is_pressed()
&& ( && (
character.movement == Stand character.movement == Stand
//|| character.movement == Run //|| character.movement == Run
@ -237,13 +237,13 @@ impl<'a> System<'a> for Sys {
kind: item::Tool::Debug(item::Debug::Boost), kind: item::Tool::Debug(item::Debug::Boost),
.. ..
}) => { }) => {
if inputs.primary { if inputs.primary.is_pressed() {
local_emitter.emit(LocalEvent::Boost { local_emitter.emit(LocalEvent::Boost {
entity, entity,
vel: inputs.look_dir * 7.0, vel: inputs.look_dir * 7.0,
}); });
} }
if inputs.secondary { if inputs.secondary.is_pressed() {
// Go upward // Go upward
local_emitter.emit(LocalEvent::Boost { local_emitter.emit(LocalEvent::Boost {
entity, entity,
@ -255,7 +255,7 @@ impl<'a> System<'a> for Sys {
kind: item::Tool::Debug(item::Debug::Possess), kind: item::Tool::Debug(item::Debug::Possess),
.. ..
}) => { }) => {
if inputs.primary if inputs.primary.is_pressed()
&& (character.movement == Stand && (character.movement == Stand
|| character.movement == Run || character.movement == Run
|| character.movement == Jump) || character.movement == Jump)
@ -288,14 +288,14 @@ impl<'a> System<'a> for Sys {
} }
} }
// Block // Block
if inputs.secondary if inputs.secondary.is_pressed()
&& (character.movement == Stand || character.movement == Run) && (character.movement == Stand || character.movement == Run)
&& character.action.is_wield() && character.action.is_wield()
{ {
character.action = Block { character.action = Block {
time_left: Duration::from_secs(5), time_left: Duration::from_secs(5),
}; };
} else if !inputs.secondary && character.action.is_block() { } else if !inputs.secondary.is_pressed() && character.action.is_block() {
character.action = Wield { character.action = Wield {
time_left: Duration::default(), time_left: Duration::default(),
}; };
@ -304,7 +304,7 @@ impl<'a> System<'a> for Sys {
// All other tools // All other tools
Some(ItemKind::Tool { .. }) => { Some(ItemKind::Tool { .. }) => {
// Attack // Attack
if inputs.primary if inputs.primary.is_pressed()
&& (character.movement == Stand && (character.movement == Stand
|| character.movement == Run || character.movement == Run
|| character.movement == Jump) || character.movement == Jump)
@ -320,14 +320,14 @@ impl<'a> System<'a> for Sys {
} }
// Block // Block
if inputs.secondary if inputs.secondary.is_pressed()
&& (character.movement == Stand || character.movement == Run) && (character.movement == Stand || character.movement == Run)
&& character.action.is_wield() && character.action.is_wield()
{ {
character.action = Block { character.action = Block {
time_left: Duration::from_secs(5), time_left: Duration::from_secs(5),
}; };
} else if !inputs.secondary && character.action.is_block() { } else if !inputs.secondary.is_pressed() && character.action.is_block() {
character.action = Wield { character.action = Wield {
time_left: Duration::default(), time_left: Duration::default(),
}; };
@ -335,7 +335,7 @@ impl<'a> System<'a> for Sys {
} }
None => { None => {
// Attack // Attack
if inputs.primary if inputs.primary.is_pressed()
&& (character.movement == Stand && (character.movement == Stand
|| character.movement == Run || character.movement == Run
|| character.movement == Jump) || character.movement == Jump)
@ -351,7 +351,7 @@ impl<'a> System<'a> for Sys {
} }
// Roll // Roll
if inputs.roll if inputs.roll.is_pressed()
&& (character.action == Idle || character.action.is_wield()) && (character.action == Idle || character.action.is_wield())
&& character.movement == Run && character.movement == Run
&& physics.on_ground && physics.on_ground
@ -362,19 +362,26 @@ impl<'a> System<'a> for Sys {
} }
// Jump // Jump
if (inputs.jump && physics.on_ground && vel.0.z <= 0.0 && !character.movement.is_roll()) if (inputs.jump.is_pressed() && !inputs.jump.is_held_down())
|| (inputs.jump && character.movement == Swim) && physics.on_ground
&& vel.0.z <= 0.0
&& !character.movement.is_roll()
|| (inputs.jump.is_pressed() && character.movement == Swim)
{ {
local_emitter.emit(LocalEvent::Jump(entity)); local_emitter.emit(LocalEvent::Jump(entity));
} }
// Wall leap // Wall leap
if inputs.wall_leap { if inputs.wall_leap.is_pressed() {
if let (Some(_wall_dir), Climb) = (physics.on_wall, character.movement) { if let (Some(_wall_dir), Climb) = (physics.on_wall, character.movement) {
//local_emitter.emit(LocalEvent::WallLeap { entity, wall_dir }); //local_emitter.emit(LocalEvent::WallLeap { entity, wall_dir });
} }
} }
if let (true, Wield { .. }) = (inputs.toggle_wield.is_pressed(), character.action) {
character.action = Idle;
}
// Process controller events // Process controller events
for event in controller.events.drain(..) { for event in controller.events.drain(..) {
match event { match event {

View File

@ -197,12 +197,13 @@ impl<'a> System<'a> for Sys {
// Climb // Climb
if let (true, Some(_wall_dir)) = ( if let (true, Some(_wall_dir)) = (
(inputs.climb | inputs.climb_down) && vel.0.z <= CLIMB_SPEED, (inputs.climb.is_pressed() | inputs.climb_down.is_pressed())
&& vel.0.z <= CLIMB_SPEED,
physics.on_wall, physics.on_wall,
) { ) {
if inputs.climb_down && !inputs.climb { if inputs.climb_down.is_pressed() && !inputs.climb.is_pressed() {
vel.0 -= dt.0 * vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0); vel.0 -= dt.0 * vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0);
} else if inputs.climb && !inputs.climb_down { } else if inputs.climb.is_pressed() && !inputs.climb_down.is_pressed() {
vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED); vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED);
} else { } else {
vel.0.z = vel.0.z + dt.0 * GRAVITY * 1.5; vel.0.z = vel.0.z + dt.0 * GRAVITY * 1.5;

View File

@ -54,6 +54,7 @@ impl SessionState {
impl SessionState { impl SessionState {
/// Tick the session (and the client attached to it). /// Tick the session (and the client attached to it).
fn tick(&mut self, dt: Duration) -> Result<(), Error> { fn tick(&mut self, dt: Duration) -> Result<(), Error> {
self.inputs.tick(dt);
for event in self.client.borrow_mut().tick(self.inputs.clone(), dt)? { for event in self.client.borrow_mut().tick(self.inputs.clone(), dt)? {
match event { match event {
Chat { Chat {
@ -177,12 +178,12 @@ impl PlayState for SessionState {
client.place_block(build_pos, self.selected_block); client.place_block(build_pos, self.selected_block);
} }
} else { } else {
self.inputs.primary = state self.inputs.primary.set_state(state);
} }
} }
Event::InputUpdate(GameInput::Secondary, state) => { Event::InputUpdate(GameInput::Secondary, state) => {
self.inputs.secondary = false; // To be changed later on self.inputs.secondary.set_state(false); // To be changed later on
let mut client = self.client.borrow_mut(); let mut client = self.client.borrow_mut();
@ -203,7 +204,7 @@ impl PlayState for SessionState {
.map(|cs| cs.action.is_wield()) .map(|cs| cs.action.is_wield())
.unwrap_or(false) .unwrap_or(false)
{ {
self.inputs.secondary = state; self.inputs.secondary.set_state(state);
} else { } else {
if let Some(select_pos) = select_pos { if let Some(select_pos) = select_pos {
client.collect_block(select_pos); client.collect_block(select_pos);
@ -226,30 +227,34 @@ impl PlayState for SessionState {
} }
} }
} else { } else {
self.inputs.roll = state; self.inputs.roll.set_state(state);
} }
} }
Event::InputUpdate(GameInput::Respawn, state) => { Event::InputUpdate(GameInput::Respawn, state) => {
self.inputs.respawn = state; self.inputs.respawn.set_state(state);
} }
Event::InputUpdate(GameInput::Jump, state) => { Event::InputUpdate(GameInput::Jump, state) => {
self.inputs.jump = state; self.inputs.jump.set_state(state);
} }
Event::InputUpdate(GameInput::Sit, state) => { Event::InputUpdate(GameInput::Sit, state) => {
self.inputs.sit = state; self.inputs.sit.set_state(state);
} }
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) => { Event::InputUpdate(GameInput::Glide, state) => {
self.inputs.glide = state; self.inputs.glide.set_state(state);
}
Event::InputUpdate(GameInput::Climb, state) => {
self.inputs.climb.set_state(state)
} }
Event::InputUpdate(GameInput::Climb, state) => self.inputs.climb = state,
Event::InputUpdate(GameInput::ClimbDown, state) => { Event::InputUpdate(GameInput::ClimbDown, state) => {
self.inputs.climb_down = state self.inputs.climb_down.set_state(state)
}
Event::InputUpdate(GameInput::WallLeap, state) => {
self.inputs.wall_leap.set_state(state)
} }
Event::InputUpdate(GameInput::WallLeap, state) => self.inputs.wall_leap = state,
Event::InputUpdate(GameInput::Mount, true) => { Event::InputUpdate(GameInput::Mount, true) => {
let mut client = self.client.borrow_mut(); let mut client = self.client.borrow_mut();
if client.is_mounted() { if client.is_mounted() {
@ -315,6 +320,9 @@ impl PlayState for SessionState {
} }
} }
} }
Event::InputUpdate(GameInput::ToggleWield, state) => {
self.inputs.toggle_wield.set_state(state)
}
// Pass all other events to the scene // Pass all other events to the scene
event => { event => {

View File

@ -47,6 +47,7 @@ pub struct ControlSettings {
pub roll: KeyMouse, pub roll: KeyMouse,
pub respawn: KeyMouse, pub respawn: KeyMouse,
pub interact: KeyMouse, pub interact: KeyMouse,
pub toggle_wield: KeyMouse,
} }
impl Default for ControlSettings { impl Default for ControlSettings {
@ -85,6 +86,7 @@ impl Default for ControlSettings {
roll: KeyMouse::Mouse(MouseButton::Middle), roll: KeyMouse::Mouse(MouseButton::Middle),
respawn: KeyMouse::Mouse(MouseButton::Left), respawn: KeyMouse::Mouse(MouseButton::Left),
interact: KeyMouse::Key(VirtualKeyCode::E), interact: KeyMouse::Key(VirtualKeyCode::E),
toggle_wield: KeyMouse::Key(VirtualKeyCode::T),
} }
} }
} }

View File

@ -44,6 +44,7 @@ pub enum GameInput {
Roll, Roll,
Respawn, Respawn,
Interact, Interact,
ToggleWield,
} }
/// Represents an incoming event from the window. /// Represents an incoming event from the window.
@ -221,6 +222,9 @@ impl Window {
map.entry(settings.controls.interact) map.entry(settings.controls.interact)
.or_default() .or_default()
.push(GameInput::Interact); .push(GameInput::Interact);
map.entry(settings.controls.toggle_wield)
.or_default()
.push(GameInput::ToggleWield);
let keypress_map = HashMap::new(); let keypress_map = HashMap::new();