From 83e4cdfe768d0badbe1746f3479e33fa9aff292a Mon Sep 17 00:00:00 2001 From: Cat Stevens Date: Mon, 20 Feb 2023 20:55:49 -0500 Subject: [PATCH] Allow locking camera zoom Allow the camera's zoom to be locked by a keybind or a Gameplay setting. The zoom lock behavior can be changed between Toggle and Auto, where Auto only locks the camera zoom while specific movement/combat inputs are being pressed. (closes !1528) A temporary fading notification is shown at the top of the screen, informing the player of the setting change (when the keybind is used) or that the zoom is locked (when the player might have forgotten the zoom is locked, and is trying to zoom). i18n strings are added for English and German, but no other languages. To implement the simplistic fading text, the behavior was extracted into an impl called `ChangeNotification`, where reasons are quantified by `NotificationReason`. --- CHANGELOG.md | 3 + assets/voxygen/i18n/de_DE/gameinput.ftl | 1 + assets/voxygen/i18n/de_DE/hud/misc.ftl | 3 + assets/voxygen/i18n/de_DE/hud/settings.ftl | 4 + assets/voxygen/i18n/en/gameinput.ftl | 1 + assets/voxygen/i18n/en/hud/misc.ftl | 3 + assets/voxygen/i18n/en/hud/settings.ftl | 4 + voxygen/src/game_input.rs | 2 + voxygen/src/hud/change_notification.rs | 86 ++++++++++++++++ voxygen/src/hud/mod.rs | 57 +++++++++++ voxygen/src/hud/settings_window/gameplay.rs | 68 ++++++++++++- voxygen/src/session/mod.rs | 103 +++++++++++++++++++- voxygen/src/session/settings_change.rs | 12 ++- voxygen/src/settings/control.rs | 1 + voxygen/src/settings/gameplay.rs | 6 +- 15 files changed, 347 insertions(+), 7 deletions(-) create mode 100644 voxygen/src/hud/change_notification.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f2deec77f2..9d59b08a17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Unlockable door blocks. - Sprite rotation for Spots. - Better entity placement options for spots. +- Camera zoom can now be locked, to prevent accidental zooming while rolling in combat. It comes + with a keybind to enable/disable the setting, and an Auto/Toggle behavior setting. Auto behavior + will only lock the camera zoom while movement and combat inputs are also being pressed. ### Changed - Bats move slower and use a simple proportional controller to maintain altitude diff --git a/assets/voxygen/i18n/de_DE/gameinput.ftl b/assets/voxygen/i18n/de_DE/gameinput.ftl index 00f8f0d167..70e99afb84 100644 --- a/assets/voxygen/i18n/de_DE/gameinput.ftl +++ b/assets/voxygen/i18n/de_DE/gameinput.ftl @@ -49,6 +49,7 @@ gameinput-togglewield = Waffe ziehen/wegstecken gameinput-interact = Interagieren gameinput-freelook = Freie Sicht gameinput-autowalk = Automatisch Laufen/Schwimmen +gameinput-zoomlock = Kamera-Zoomsperre gameinput-cameraclamp = Kamera fixieren gameinput-dance = Tanzen gameinput-select = Einheit auswählen diff --git a/assets/voxygen/i18n/de_DE/hud/misc.ftl b/assets/voxygen/i18n/de_DE/hud/misc.ftl index c6259a2a68..53fd52efac 100644 --- a/assets/voxygen/i18n/de_DE/hud/misc.ftl +++ b/assets/voxygen/i18n/de_DE/hud/misc.ftl @@ -35,6 +35,9 @@ hud-diary = Tagebuch hud-free_look_indicator = Freie Sicht aktiv. Drücke { $key } zum deaktivieren. hud-camera_clamp_indicator = Vertikale Kamerafixierung aktiv. Drücke { $key } zum deaktivieren. hud-auto_walk_indicator = Auto Laufen/Schwimmen aktiv +hud-zoom_lock_indicator-remind = Zoom gesperrt +hud-zoom_lock_indicator-enable = Kamerazoom gesperrt +hud-zoom_lock_indicator-disable = Kamerazoom entsperrt hud-collect = Aufsammeln hud-pick_up = Aufheben hud-open = Öffnen diff --git a/assets/voxygen/i18n/de_DE/hud/settings.ftl b/assets/voxygen/i18n/de_DE/hud/settings.ftl index 64a3fe0bdc..989b2c7ff5 100644 --- a/assets/voxygen/i18n/de_DE/hud/settings.ftl +++ b/assets/voxygen/i18n/de_DE/hud/settings.ftl @@ -2,6 +2,8 @@ hud-settings-general = Allgemein hud-settings-none = Keine hud-settings-press_behavior-toggle = Umschalten hud-settings-press_behavior-hold = Halten +hud-settings-autopress_behavior-toggle = Umschalten +hud-settings-autopress_behavior-auto = Auto hud-settings-help_window = Hilfe zu Fenstern hud-settings-debug_info = Debug-Informationen hud-settings-show_hitboxes = Hitboxen anzeigen @@ -50,10 +52,12 @@ hud-settings-enable_mouse_smoothing = Kamera-Glättung hud-settings-free_look_behavior = Verhalten bei freiem Kameramodus hud-settings-auto_walk_behavior = Verhalten bei automatischem Gehen hud-settings-camera_clamp_behavior = Verhalten bei starrer Kamera +hud-settings-zoom_lock_behavior = Verhalten bei Kamera-Zoomsperre hud-settings-player_physics_behavior = Spielerphysik (experimentell) hud-settings-stop_auto_walk_on_input = Automatisches Gehen bei Spieleraktivität anhalten hud-settings-auto_camera = Auto Kamera hud-settings-bow_zoom = Hinein zoomen, wenn der Bogen aufgeladen wird +hud-settings-zoom_lock = Kamera-Zoomsperre hud-settings-reset_gameplay = Einstellungen zurücksetzen diff --git a/assets/voxygen/i18n/en/gameinput.ftl b/assets/voxygen/i18n/en/gameinput.ftl index f8900e24b2..7237382e62 100644 --- a/assets/voxygen/i18n/en/gameinput.ftl +++ b/assets/voxygen/i18n/en/gameinput.ftl @@ -49,6 +49,7 @@ gameinput-togglewield = Toggle Wield gameinput-interact = Interact gameinput-freelook = Free Look gameinput-autowalk = Auto Walk/Swim +gameinput-zoomlock = Camera zoom lock gameinput-cameraclamp = Camera Clamp gameinput-dance = Dance gameinput-select = Select Entity diff --git a/assets/voxygen/i18n/en/hud/misc.ftl b/assets/voxygen/i18n/en/hud/misc.ftl index ef5d48c584..54c4a4612a 100644 --- a/assets/voxygen/i18n/en/hud/misc.ftl +++ b/assets/voxygen/i18n/en/hud/misc.ftl @@ -35,6 +35,9 @@ hud-diary = Diary hud-free_look_indicator = Free look active. Press { $key } to disable. hud-camera_clamp_indicator = Camera vertical clamp active. Press { $key } to disable. hud-auto_walk_indicator = Auto walk/swim active +hud-zoom_lock_indicator-remind = Zoom locked +hud-zoom_lock_indicator-enable = Camera zoom locked +hud-zoom_lock_indicator-disable = Camera zoom unlocked hud-collect = Collect hud-pick_up = Pick up hud-open = Open diff --git a/assets/voxygen/i18n/en/hud/settings.ftl b/assets/voxygen/i18n/en/hud/settings.ftl index 9504ed2f2c..0575de807b 100644 --- a/assets/voxygen/i18n/en/hud/settings.ftl +++ b/assets/voxygen/i18n/en/hud/settings.ftl @@ -2,6 +2,8 @@ hud-settings-general = General hud-settings-none = None hud-settings-press_behavior-toggle = Toggle hud-settings-press_behavior-hold = Hold +hud-settings-autopress_behavior-toggle = Toggle +hud-settings-autopress_behavior-auto = Auto hud-settings-help_window = Help Window hud-settings-debug_info = Debug Info hud-settings-show_hitboxes = Show hitboxes @@ -49,10 +51,12 @@ hud-settings-enable_mouse_smoothing = Camera Smoothing hud-settings-free_look_behavior = Free look behavior hud-settings-auto_walk_behavior = Auto walk behavior hud-settings-camera_clamp_behavior = Camera clamp behavior +hud-settings-zoom_lock_behavior = Camera zoom lock behavior hud-settings-player_physics_behavior = Player physics (experimental) hud-settings-stop_auto_walk_on_input = Stop auto walk on movement hud-settings-auto_camera = Auto camera hud-settings-bow_zoom = Zoom in when charging bow +hud-settings-zoom_lock = Camera zoom lock hud-settings-reset_gameplay = Reset to Defaults hud-settings-view_distance = View Distance hud-settings-entity_view_distance = Entities View Distance diff --git a/voxygen/src/game_input.rs b/voxygen/src/game_input.rs index b9902f9fdc..80ca3afeee 100644 --- a/voxygen/src/game_input.rs +++ b/voxygen/src/game_input.rs @@ -132,6 +132,8 @@ pub enum GameInput { FreeLook, #[strum(serialize = "gameinput-autowalk")] AutoWalk, + #[strum(serialize = "gameinput-zoomlock")] + ZoomLock, #[strum(serialize = "gameinput-cameraclamp")] CameraClamp, #[strum(serialize = "gameinput-cyclecamera")] diff --git a/voxygen/src/hud/change_notification.rs b/voxygen/src/hud/change_notification.rs new file mode 100644 index 0000000000..e05c6712a7 --- /dev/null +++ b/voxygen/src/hud/change_notification.rs @@ -0,0 +1,86 @@ +use serde::{Deserialize, Serialize}; + +use std::time::Duration; + +/// Default initial alpha of a Notify +const NOTIF_START_ALPHA: f32 = 1.0; + +/// Default time to live of a notify +const NOTIF_LIFETIME: f32 = 2.0; +/// Default fading time of a notify +const NOTIF_FADETIME: f32 = 1.5; + +/// The reason this notification is being shown: the setting is being enabled or +/// disabled, or we are reminding the player of the current state. +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +pub enum NotificationReason { + Remind = 2, + Enable = 1, + #[serde(other)] + Disable = 0, +} +/// A temporal, fading message that a setting +/// or other state was changed (probably by direct player input) +#[derive(Default)] +pub struct ChangeNotification { + pub reason: Option, + pub alpha: f32, + lifetime: Duration, + fadetime: Duration, + initial_fadetime: Duration, +} + +impl ChangeNotification { + pub fn new( + reason: Option, + alpha: f32, + lifetime: Duration, + fadetime: Duration, + ) -> Result { + if fadetime.is_zero() { + Err(fadetime) + } else { + Ok(Self { + reason, + alpha, + lifetime, + fadetime, + initial_fadetime: fadetime, + }) + } + } + + pub fn from_reason(reason: NotificationReason) -> Self { + ChangeNotification::new( + Some(reason), + NOTIF_START_ALPHA, + Duration::from_secs_f32(NOTIF_LIFETIME), + Duration::from_secs_f32(NOTIF_FADETIME), + ) + .unwrap() + } + + pub fn from_state(state: bool) -> Self { + ChangeNotification::from_reason(match state { + true => NotificationReason::Enable, + false => NotificationReason::Disable, + }) + } + + pub fn update(&mut self, dt: Duration) { + if self.reason.is_some() { + // Timer before fade + if !self.lifetime.is_zero() { + self.lifetime = self.lifetime.saturating_sub(dt); + // Lifetime expired, start to fade + } else if !self.fadetime.is_zero() { + self.fadetime = self.fadetime.saturating_sub(dt); + // alpha as elapsed duration fraction, multiply with this for nice fade curve + self.alpha = self.fadetime.as_secs_f32() / self.initial_fadetime.as_secs_f32(); + // Done fading + } else { + self.reason = None; + } + } + } +} diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 3396449483..adaefa381f 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2,6 +2,7 @@ mod animation; mod bag; mod buffs; mod buttons; +mod change_notification; mod chat; mod crafting; mod diary; @@ -33,6 +34,7 @@ pub use settings_window::ScaleChange; use bag::Bag; use buffs::BuffsBar; use buttons::Buttons; +use change_notification::{ChangeNotification, NotificationReason}; use chat::Chat; use chrono::NaiveTime; use crafting::Crafting; @@ -329,6 +331,10 @@ widget_ids! { auto_walk_txt, auto_walk_bg, + // Temporal (fading) camera zoom lock indicator + zoom_lock_txt, + zoom_lock_bg, + // Camera clamp indicator camera_clamp_txt, camera_clamp_bg, @@ -797,6 +803,16 @@ pub enum PressBehavior { #[serde(other)] Toggle = 0, } +/// Similar to [PressBehavior], with different semantics for settings that +/// change state automatically. There is no [PressBehavior::update][update] +/// implementation because it doesn't apply to the use case; this is just a +/// sentinel. +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum AutoPressBehavior { + Auto = 1, + #[serde(other)] + Toggle = 0, +} #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct ChatTab { pub label: String, @@ -889,6 +905,7 @@ pub struct Show { stats: bool, free_look: bool, auto_walk: bool, + zoom_lock: ChangeNotification, camera_clamp: bool, prompt_dialog: Option, location_markers: MapMarkers, @@ -1367,6 +1384,7 @@ impl Hud { stats: false, free_look: false, auto_walk: false, + zoom_lock: ChangeNotification::default(), camera_clamp: false, prompt_dialog: None, location_markers: MapMarkers::default(), @@ -3522,6 +3540,31 @@ impl Hud { .set(self.ids.auto_walk_txt, ui_widgets); } + // Camera zoom lock + self.show.zoom_lock.update(dt); + + if let Some(zoom_lock) = self.show.zoom_lock.reason { + let zoom_lock_message = match zoom_lock { + NotificationReason::Remind => "hud-zoom_lock_indicator-remind", + NotificationReason::Enable => "hud-zoom_lock_indicator-enable", + NotificationReason::Disable => "hud-zoom_lock_indicator-disable", + }; + + Text::new(&i18n.get_msg(zoom_lock_message)) + .color(TEXT_BG.alpha(self.show.zoom_lock.alpha)) + .mid_top_with_margin_on(ui_widgets.window, indicator_offset) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(20)) + .set(self.ids.zoom_lock_bg, ui_widgets); + indicator_offset += 30.0; + Text::new(&i18n.get_msg(zoom_lock_message)) + .color(TEXT_COLOR.alpha(self.show.zoom_lock.alpha)) + .top_left_with_margins_on(self.ids.zoom_lock_bg, -1.0, -1.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(20)) + .set(self.ids.zoom_lock_txt, ui_widgets); + } + // Camera clamp indicator if let Some(cameraclamp_key) = global_state .settings @@ -4562,6 +4605,20 @@ impl Hud { pub fn camera_clamp(&mut self, camera_clamp: bool) { self.show.camera_clamp = camera_clamp; } + /// Remind the player camera zoom is currently locked, for example if they + /// are trying to zoom. + pub fn zoom_lock_reminder(&mut self) { + if self.show.zoom_lock.reason.is_none() { + self.show.zoom_lock = ChangeNotification::from_reason(NotificationReason::Remind); + } + } + + /// Start showing a temporary notification ([ChangeNotification]) that zoom + /// lock was toggled on/off. + pub fn zoom_lock_toggle(&mut self, state: bool) { + self.show.zoom_lock = ChangeNotification::from_state(state); + } + pub fn handle_outcome( &mut self, outcome: &Outcome, diff --git a/voxygen/src/hud/settings_window/gameplay.rs b/voxygen/src/hud/settings_window/gameplay.rs index 4f0ddd5e13..666f8ba573 100644 --- a/voxygen/src/hud/settings_window/gameplay.rs +++ b/voxygen/src/hud/settings_window/gameplay.rs @@ -1,7 +1,7 @@ use super::{RESET_BUTTONS_HEIGHT, RESET_BUTTONS_WIDTH}; use crate::{ - hud::{img_ids::Imgs, PressBehavior, MENU_BG, TEXT_COLOR}, + hud::{img_ids::Imgs, AutoPressBehavior, PressBehavior, MENU_BG, TEXT_COLOR}, session::settings_change::{Gameplay as GameplayChange, Gameplay::*}, ui::{fonts::Fonts, ImageSlider, ToggleButton}, GlobalState, @@ -44,12 +44,16 @@ widget_ids! { auto_walk_behavior_list, camera_clamp_behavior_text, camera_clamp_behavior_list, + zoom_lock_behavior_text, + zoom_lock_behavior_list, stop_auto_walk_on_input_button, stop_auto_walk_on_input_label, auto_camera_button, auto_camera_label, bow_zoom_button, bow_zoom_label, + zoom_lock_button, + zoom_lock_label, } } @@ -528,6 +532,68 @@ impl<'a> Widget for Gameplay<'a> { .color(TEXT_COLOR) .set(state.ids.bow_zoom_label, ui); + let zoom_lock_label_list = [ + self.localized_strings + .get_msg("hud-settings-autopress_behavior-toggle"), + self.localized_strings + .get_msg("hud-settings-autopress_behavior-auto"), + ]; + + // Camera zoom lock behavior + Text::new( + &self + .localized_strings + .get_msg("hud-settings-zoom_lock_behavior"), + ) + .down_from(state.ids.auto_walk_behavior_list, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.zoom_lock_behavior_text, ui); + + let zoom_lock_selected = self.global_state.settings.gameplay.zoom_lock_behavior as usize; + + if let Some(clicked) = DropDownList::new(&zoom_lock_label_list, Some(zoom_lock_selected)) + .w_h(200.0, 30.0) + .color(MENU_BG) + .label_color(TEXT_COLOR) + .label_font_id(self.fonts.cyri.conrod_id) + .down_from(state.ids.zoom_lock_behavior_text, 8.0) + .set(state.ids.zoom_lock_behavior_list, ui) + { + match clicked { + 0 => events.push(ChangeZoomLockBehavior(AutoPressBehavior::Toggle)), + 1 => events.push(ChangeZoomLockBehavior(AutoPressBehavior::Auto)), + _ => unreachable!(), + } + } + + // Camera zoom lock toggle + let zoom_lock_toggle = ToggleButton::new( + self.global_state.settings.gameplay.zoom_lock, + self.imgs.checkbox, + self.imgs.checkbox_checked, + ) + .w_h(18.0, 18.0) + .down_from(state.ids.bow_zoom_button, 8.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.zoom_lock_button, ui); + + if self.global_state.settings.gameplay.zoom_lock != zoom_lock_toggle { + events.push(ChangeZoomLock( + !self.global_state.settings.gameplay.zoom_lock, + )); + } + + Text::new(&self.localized_strings.get_msg("hud-settings-zoom_lock")) + .right_from(state.ids.zoom_lock_button, 10.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .graphics_for(state.ids.zoom_lock_button) + .color(TEXT_COLOR) + .set(state.ids.zoom_lock_label, ui); + // Reset the gameplay settings to the default settings if Button::image(self.imgs.button) .w_h(RESET_BUTTONS_WIDTH, RESET_BUTTONS_HEIGHT) diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index f256f9a1e9..073728b03b 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -43,8 +43,8 @@ use crate::{ error::Error, game_input::GameInput, hud::{ - DebugInfo, Event as HudEvent, Hud, HudCollectFailedReason, HudInfo, LootMessage, - PromptDialogSettings, + AutoPressBehavior, DebugInfo, Event as HudEvent, Hud, HudCollectFailedReason, HudInfo, + LootMessage, PromptDialogSettings, }, key_state::KeyState, menu::char_selection::CharSelectionState, @@ -61,6 +61,25 @@ use target::targets_under_cursor; #[cfg(feature = "egui-ui")] use voxygen_egui::EguiDebugInfo; +/** The zoom scroll delta that is considered an "intent" + to zoom, rather than the accidental zooming that Zoom Lock + is supposed to help. + This is used for both [AutoPressBehaviors::Toggle] and [AutoPressBehaviors::Auto]. + + This value should likely differ between trackpad scrolling + and various mouse wheels, but we just choose a reasonable + default. + + All the mice I have can only scroll at |delta|=15 no matter + how fast, I guess the default should be less than that so + it gets seen. This could possibly be a user setting changed + only in a config file; it's too minor to put in the GUI. + If a player reports that their scroll wheel is apparently not + working, this value may be to blame (i.e. their intent to scroll + is not being detected at a low enough scroll speed). +*/ +const ZOOM_LOCK_SCROLL_DELTA_INTENT: f32 = 14.0; + /// The action to perform after a tick enum TickAction { // Continue executing @@ -83,6 +102,7 @@ pub struct SessionState { free_look: bool, auto_walk: bool, camera_clamp: bool, + zoom_lock: bool, is_aiming: bool, target_entity: Option, selected_entity: Option<(specs::Entity, std::time::Instant)>, @@ -152,6 +172,7 @@ impl SessionState { free_look: false, auto_walk: false, camera_clamp: false, + zoom_lock: false, is_aiming: false, target_entity: None, selected_entity: None, @@ -171,6 +192,24 @@ impl SessionState { self.key_state.auto_walk = false; } + /// Possibly lock the camera zoom depending on the current behaviour, and + /// the current inputs if in the Auto state. + fn maybe_auto_zoom_lock( + &mut self, + zoom_lock_enabled: bool, + zoom_lock_behavior: AutoPressBehavior, + ) { + if let AutoPressBehavior::Auto = zoom_lock_behavior { + // to add Analog detection, update the condition rhs with a check for + // MovementX/Y event from the last tick + self.zoom_lock = zoom_lock_enabled && self.should_auto_zoom_lock(); + } else { + // it's intentional that the HUD notification is not shown in this case: + // refresh session from Settings HUD checkbox change + self.zoom_lock = zoom_lock_enabled; + } + } + /// Gets the entity that is the current viewpoint, and a bool if the client /// is allowed to edit it's data. fn viewpoint_entity(&self) -> (specs::Entity, bool) { @@ -406,6 +445,45 @@ impl SessionState { /// Clean up the session (and the client attached to it) after a tick. pub fn cleanup(&mut self) { self.client.borrow_mut().cleanup(); } + + fn should_auto_zoom_lock(&self) -> bool { + let inputs_state = &self.inputs_state; + for input in inputs_state { + match input { + GameInput::Primary + | GameInput::Secondary + | GameInput::Block + | GameInput::MoveForward + | GameInput::MoveLeft + | GameInput::MoveRight + | GameInput::MoveBack + | GameInput::Jump + | GameInput::Roll + | GameInput::Sneak + | GameInput::AutoWalk + | GameInput::Climb + | GameInput::ClimbDown + | GameInput::SwimUp + | GameInput::SwimDown + | GameInput::SwapLoadout + | GameInput::ToggleWield + | GameInput::Slot1 + | GameInput::Slot2 + | GameInput::Slot3 + | GameInput::Slot4 + | GameInput::Slot5 + | GameInput::Slot6 + | GameInput::Slot7 + | GameInput::Slot8 + | GameInput::Slot9 + | GameInput::Slot10 + | GameInput::SpectateViewpoint + | GameInput::SpectateSpeedBoost => return true, + _ => (), + } + } + false + } } impl PlayState for SessionState { @@ -547,6 +625,11 @@ impl PlayState for SessionState { drop(client); + self.maybe_auto_zoom_lock( + global_state.settings.gameplay.zoom_lock, + global_state.settings.gameplay.zoom_lock_behavior, + ); + if presence == PresenceKind::Spectator { let mut client = self.client.borrow_mut(); if client.spectate_position(cam_pos) { @@ -609,7 +692,6 @@ impl PlayState for SessionState { continue; } } - match event { Event::Close => { return PlayStateResult::Shutdown; @@ -950,6 +1032,14 @@ impl PlayState for SessionState { self.key_state.auto_walk = self.auto_walk && !self.client.borrow().is_gliding(); }, + GameInput::ZoomLock => { + if state { + global_state.settings.gameplay.zoom_lock ^= true; + + self.hud + .zoom_lock_toggle(global_state.settings.gameplay.zoom_lock); + } + }, GameInput::CameraClamp => { let hud = &mut self.hud; global_state.settings.gameplay.camera_clamp_behavior.update( @@ -1032,6 +1122,13 @@ impl PlayState for SessionState { message: screenshot_message, }), + Event::Zoom(delta) if self.zoom_lock => { + // only fire this Hud event when player has "intent" to zoom + if delta.abs() > ZOOM_LOCK_SCROLL_DELTA_INTENT { + self.hud.zoom_lock_reminder(); + } + }, + // Pass all other events to the scene event => { self.scene.handle_input_event(event, &self.client.borrow()); diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index 82c630f5d1..429eed96b1 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -3,8 +3,8 @@ use crate::{ controller::ControllerSettings, game_input::GameInput, hud::{ - BarNumbers, BuffPosition, ChatTab, CrosshairType, Intro, PressBehavior, ScaleChange, - ShortcutNumbers, XpBar, + AutoPressBehavior, BarNumbers, BuffPosition, ChatTab, CrosshairType, Intro, PressBehavior, + ScaleChange, ShortcutNumbers, XpBar, }, render::RenderMode, settings::{ @@ -68,9 +68,11 @@ pub enum Gameplay { ChangeFreeLookBehavior(PressBehavior), ChangeAutoWalkBehavior(PressBehavior), ChangeCameraClampBehavior(PressBehavior), + ChangeZoomLockBehavior(AutoPressBehavior), ChangeStopAutoWalkOnInput(bool), ChangeAutoCamera(bool), ChangeBowZoom(bool), + ChangeZoomLock(bool), ResetGameplaySettings, } @@ -386,6 +388,9 @@ impl SettingsChange { Gameplay::ChangeCameraClampBehavior(behavior) => { settings.gameplay.camera_clamp_behavior = behavior; }, + Gameplay::ChangeZoomLockBehavior(state) => { + settings.gameplay.zoom_lock_behavior = state; + }, Gameplay::ChangeStopAutoWalkOnInput(state) => { settings.gameplay.stop_auto_walk_on_input = state; }, @@ -395,6 +400,9 @@ impl SettingsChange { Gameplay::ChangeBowZoom(state) => { settings.gameplay.bow_zoom = state; }, + Gameplay::ChangeZoomLock(state) => { + settings.gameplay.zoom_lock = state; + }, Gameplay::ResetGameplaySettings => { // Reset Gameplay Settings settings.gameplay = GameplaySettings::default(); diff --git a/voxygen/src/settings/control.rs b/voxygen/src/settings/control.rs index 43957f8624..e064593a62 100644 --- a/voxygen/src/settings/control.rs +++ b/voxygen/src/settings/control.rs @@ -170,6 +170,7 @@ impl ControlSettings { GameInput::ToggleWield => Some(KeyMouse::Key(VirtualKeyCode::R)), GameInput::FreeLook => Some(KeyMouse::Key(VirtualKeyCode::L)), GameInput::AutoWalk => Some(KeyMouse::Key(VirtualKeyCode::Period)), + GameInput::ZoomLock => Some(KeyMouse::Key(VirtualKeyCode::Semicolon)), GameInput::CameraClamp => Some(KeyMouse::Key(VirtualKeyCode::Apostrophe)), GameInput::CycleCamera => Some(KeyMouse::Key(VirtualKeyCode::Key0)), GameInput::Slot1 => Some(KeyMouse::Key(VirtualKeyCode::Key1)), diff --git a/voxygen/src/settings/gameplay.rs b/voxygen/src/settings/gameplay.rs index 20a94a6a0e..6a8fe35ce1 100644 --- a/voxygen/src/settings/gameplay.rs +++ b/voxygen/src/settings/gameplay.rs @@ -1,4 +1,4 @@ -use crate::hud::PressBehavior; +use crate::hud::{AutoPressBehavior, PressBehavior}; use serde::{Deserialize, Serialize}; /// `GameplaySettings` contains sensitivity and gameplay options. @@ -14,9 +14,11 @@ pub struct GameplaySettings { pub free_look_behavior: PressBehavior, pub auto_walk_behavior: PressBehavior, pub camera_clamp_behavior: PressBehavior, + pub zoom_lock_behavior: AutoPressBehavior, pub stop_auto_walk_on_input: bool, pub auto_camera: bool, pub bow_zoom: bool, + pub zoom_lock: bool, } impl Default for GameplaySettings { @@ -31,9 +33,11 @@ impl Default for GameplaySettings { free_look_behavior: PressBehavior::Toggle, auto_walk_behavior: PressBehavior::Toggle, camera_clamp_behavior: PressBehavior::Toggle, + zoom_lock_behavior: AutoPressBehavior::Auto, stop_auto_walk_on_input: true, auto_camera: false, bow_zoom: true, + zoom_lock: false, } } }