diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 6a45bebfbc..a152da7db7 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -1,8 +1,13 @@ use specs::{Component, FlaggedStorage}; use specs_idvs::IDVStorage; use sphynx::Uid; +use std::ops::Add; +use std::time::Duration; 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)] pub enum ControlEvent { Mount(Uid), @@ -11,18 +16,101 @@ pub enum ControlEvent { //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 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)] pub struct ControllerInputs { - pub primary: bool, - pub secondary: bool, - pub sit: bool, - pub jump: bool, - pub roll: bool, - pub glide: bool, - pub climb: bool, - pub climb_down: bool, - pub wall_leap: bool, - pub respawn: bool, + pub primary: Input, + pub secondary: Input, + pub sit: Input, + pub jump: Input, + pub roll: Input, + pub glide: Input, + pub climb: Input, + pub climb_down: Input, + pub wall_leap: Input, + pub respawn: Input, + pub toggle_wield: Input, pub move_dir: Vec2, pub look_dir: Vec3, } @@ -34,7 +122,25 @@ pub struct Controller { pub events: Vec, } +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 { + /// Sets all inputs to default pub fn reset(&mut self) { *self = Self::default(); } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index fa13636e02..42f8e6e161 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -22,7 +22,8 @@ pub use body::{ }; pub use character_state::{ActionState, CharacterState, MovementState}; pub use controller::{ - ControlEvent, Controller, ControllerInputs, InventoryManip, MountState, Mounting, + ControlEvent, Controller, ControllerInputs, Input, InputState, InventoryManip, MountState, + Mounting, }; pub use inputs::CanBuild; pub use inventory::{item, Inventory, InventoryUpdate, Item, ItemKind}; diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index 67d3025286..1519f0722b 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -67,7 +67,7 @@ impl<'a> System<'a> for Sys { let tgt_pos = tgt_pos.0 + *offset; if tgt_pos.z > pos.0.z + 1.0 { - inputs.jump = true; + inputs.jump.set_state(true); } // Move towards the target. @@ -113,19 +113,19 @@ impl<'a> System<'a> for Sys { // Fight (and slowly move closer) inputs.move_dir = Vec2::::from(target_pos.0 - pos.0).normalized() * 0.01; - inputs.primary = true; + inputs.primary.set_state(true); } else if dist < SIGHT_DIST { inputs.move_dir = Vec2::::from(target_pos.0 - pos.0).normalized() * 0.96; if rand::random::() < 0.02 { - inputs.roll = true; + inputs.roll.set_state(true); } if target_character.movement == Glide && target_pos.0.z > pos.0.z + 5.0 { - inputs.glide = true; - inputs.jump = true; + inputs.glide.set_state(true); + inputs.jump.set_state(true); } } else { choose_new = true; diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index aed96a0b7d..fe9785fd73 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -70,7 +70,7 @@ impl<'a> System<'a> for Sys { if stats.is_dead { // Respawn - if inputs.respawn { + if inputs.respawn.is_pressed() { server_emitter.emit(ServerEvent::Respawn(entity)); } continue; @@ -97,19 +97,19 @@ impl<'a> System<'a> for Sys { // Glide // TODO: Check for glide ability/item - if inputs.glide + if inputs.glide.is_pressed() && !physics.on_ground && (character.action == Idle || character.action.is_wield()) && character.movement == Jump && body.is_humanoid() { character.movement = Glide; - } else if !inputs.glide && character.movement == Glide { - character.movement = Jump; + } else if !inputs.glide.is_pressed() && character.movement == Glide { + // character.movement = Jump; } // Sit - if inputs.sit + if inputs.sit.is_pressed() && physics.on_ground && character.action == Idle && character.movement != Sit @@ -123,7 +123,7 @@ impl<'a> System<'a> for Sys { } // Wield - if inputs.primary + if inputs.primary.is_pressed() && character.action == Idle && (character.movement == Stand || character.movement == Run) { @@ -138,7 +138,7 @@ impl<'a> System<'a> for Sys { power, .. }) => { - if inputs.primary + if inputs.primary.is_pressed() && (character.movement == Stand || character.movement == Run || character.movement == Jump) @@ -177,7 +177,7 @@ impl<'a> System<'a> for Sys { .. }) => { // Melee Attack - if inputs.primary + if inputs.primary.is_pressed() && (character.movement == Stand || character.movement == Run || character.movement == Jump) @@ -192,7 +192,7 @@ impl<'a> System<'a> for Sys { } } // Magical Bolt - if inputs.secondary + if inputs.secondary.is_pressed() && ( character.movement == Stand //|| character.movement == Run @@ -237,13 +237,13 @@ impl<'a> System<'a> for Sys { kind: item::Tool::Debug(item::Debug::Boost), .. }) => { - if inputs.primary { + if inputs.primary.is_pressed() { local_emitter.emit(LocalEvent::Boost { entity, vel: inputs.look_dir * 7.0, }); } - if inputs.secondary { + if inputs.secondary.is_pressed() { // Go upward local_emitter.emit(LocalEvent::Boost { entity, @@ -255,7 +255,7 @@ impl<'a> System<'a> for Sys { kind: item::Tool::Debug(item::Debug::Possess), .. }) => { - if inputs.primary + if inputs.primary.is_pressed() && (character.movement == Stand || character.movement == Run || character.movement == Jump) @@ -288,14 +288,14 @@ impl<'a> System<'a> for Sys { } } // Block - if inputs.secondary + if inputs.secondary.is_pressed() && (character.movement == Stand || character.movement == Run) && character.action.is_wield() { character.action = Block { 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 { time_left: Duration::default(), }; @@ -304,7 +304,7 @@ impl<'a> System<'a> for Sys { // All other tools Some(ItemKind::Tool { .. }) => { // Attack - if inputs.primary + if inputs.primary.is_pressed() && (character.movement == Stand || character.movement == Run || character.movement == Jump) @@ -320,14 +320,14 @@ impl<'a> System<'a> for Sys { } // Block - if inputs.secondary + if inputs.secondary.is_pressed() && (character.movement == Stand || character.movement == Run) && character.action.is_wield() { character.action = Block { 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 { time_left: Duration::default(), }; @@ -335,7 +335,7 @@ impl<'a> System<'a> for Sys { } None => { // Attack - if inputs.primary + if inputs.primary.is_pressed() && (character.movement == Stand || character.movement == Run || character.movement == Jump) @@ -351,7 +351,7 @@ impl<'a> System<'a> for Sys { } // Roll - if inputs.roll + if inputs.roll.is_pressed() && (character.action == Idle || character.action.is_wield()) && character.movement == Run && physics.on_ground @@ -362,19 +362,26 @@ impl<'a> System<'a> for Sys { } // Jump - if (inputs.jump && physics.on_ground && vel.0.z <= 0.0 && !character.movement.is_roll()) - || (inputs.jump && character.movement == Swim) + if (inputs.jump.is_pressed() && !inputs.jump.is_held_down()) + && 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)); } // Wall leap - if inputs.wall_leap { + if inputs.wall_leap.is_pressed() { if let (Some(_wall_dir), Climb) = (physics.on_wall, character.movement) { //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 for event in controller.events.drain(..) { match event { diff --git a/common/src/sys/movement.rs b/common/src/sys/movement.rs index 638d9e715c..3a46178278 100644 --- a/common/src/sys/movement.rs +++ b/common/src/sys/movement.rs @@ -197,12 +197,13 @@ impl<'a> System<'a> for Sys { // Climb 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, ) { - 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); - } 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); } else { vel.0.z = vel.0.z + dt.0 * GRAVITY * 1.5; diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 8b91ed78f8..ca422fcda1 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -54,6 +54,7 @@ impl SessionState { impl SessionState { /// Tick the session (and the client attached to it). fn tick(&mut self, dt: Duration) -> Result<(), Error> { + self.inputs.tick(dt); for event in self.client.borrow_mut().tick(self.inputs.clone(), dt)? { match event { Chat { @@ -177,12 +178,12 @@ impl PlayState for SessionState { client.place_block(build_pos, self.selected_block); } } else { - self.inputs.primary = state + self.inputs.primary.set_state(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(); @@ -203,7 +204,7 @@ impl PlayState for SessionState { .map(|cs| cs.action.is_wield()) .unwrap_or(false) { - self.inputs.secondary = state; + self.inputs.secondary.set_state(state); } else { if let Some(select_pos) = select_pos { client.collect_block(select_pos); @@ -226,30 +227,34 @@ impl PlayState for SessionState { } } } else { - self.inputs.roll = state; + self.inputs.roll.set_state(state); } } Event::InputUpdate(GameInput::Respawn, state) => { - self.inputs.respawn = state; + self.inputs.respawn.set_state(state); } Event::InputUpdate(GameInput::Jump, state) => { - self.inputs.jump = state; + self.inputs.jump.set_state(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::MoveBack, state) => self.key_state.down = state, Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state, Event::InputUpdate(GameInput::MoveRight, state) => self.key_state.right = 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) => { - 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) => { let mut client = self.client.borrow_mut(); 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 event => { diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index f33288b8c0..d37829a640 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -47,6 +47,7 @@ pub struct ControlSettings { pub roll: KeyMouse, pub respawn: KeyMouse, pub interact: KeyMouse, + pub toggle_wield: KeyMouse, } impl Default for ControlSettings { @@ -85,6 +86,7 @@ impl Default for ControlSettings { roll: KeyMouse::Mouse(MouseButton::Middle), respawn: KeyMouse::Mouse(MouseButton::Left), interact: KeyMouse::Key(VirtualKeyCode::E), + toggle_wield: KeyMouse::Key(VirtualKeyCode::T), } } } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 8f74603abf..5f39c79f4f 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -44,6 +44,7 @@ pub enum GameInput { Roll, Respawn, Interact, + ToggleWield, } /// Represents an incoming event from the window. @@ -221,6 +222,9 @@ impl Window { map.entry(settings.controls.interact) .or_default() .push(GameInput::Interact); + map.entry(settings.controls.toggle_wield) + .or_default() + .push(GameInput::ToggleWield); let keypress_map = HashMap::new();