diff --git a/CHANGELOG.md b/CHANGELOG.md index e34a0debb1..a6fc0d1ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Tweaked CR and exp calculation formula - Sprite spawn rates - The Interact button can be used on campfires to sit +- Made map icons fade out when near the edge of the map display ### Removed diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index 4f2d57ea47..1601e9feec 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -103,6 +103,7 @@ pub struct Map<'a> { rot_imgs: &'a ImgsRot, tooltip_manager: &'a mut TooltipManager, location_marker: Option>, + map_drag: Vec2, } impl<'a> Map<'a> { #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 @@ -118,6 +119,7 @@ impl<'a> Map<'a> { global_state: &'a GlobalState, tooltip_manager: &'a mut TooltipManager, location_marker: Option>, + map_drag: Vec2, ) -> Self { Self { show, @@ -132,6 +134,7 @@ impl<'a> Map<'a> { global_state, tooltip_manager, location_marker, + map_drag, } } } @@ -145,6 +148,7 @@ pub enum Event { Close, RequestSiteInfo(SiteId), SetLocationMarker(Vec2), + MapDrag(Vec2), ToggleMarker, } @@ -197,7 +201,6 @@ impl<'a> Widget for Map<'a> { fn update(self, args: widget::UpdateArgs) -> Self::Event { common_base::prof_span!("Map::update"); let widget::UpdateArgs { state, ui, .. } = args; - let drag = self.global_state.settings.interface.map_drag; let zoom = self.global_state.settings.interface.map_zoom; let show_difficulty = self.global_state.settings.interface.map_show_difficulty; let show_towns = self.global_state.settings.interface.map_show_towns; @@ -318,7 +321,7 @@ impl<'a> Widget for Map<'a> { player_pos.xy().map(|x| x as f64) / TerrainChunkSize::RECT_SIZE.map(|x| x as f64); let min_drag = player_pos_chunks - worldsize.map(|x| x as f64); let max_drag = player_pos_chunks; - let drag = drag.clamped(min_drag, max_drag); + let drag = self.map_drag.clamped(min_drag, max_drag); let handle_widget_mouse_events = |widget, wpos: Option>, @@ -360,7 +363,7 @@ impl<'a> Widget for Map<'a> { let mouse_pos = Vec2::from_slice(&cursor_pos); let drag_new = drag + mouse_pos * (1.0 / new_zoom_lvl - 1.0 / zoom); if drag_new != drag { - events.push(Event::SettingsChange(MapDrag(drag_new))); + events.push(Event::MapDrag(drag_new)); } } } @@ -375,7 +378,7 @@ impl<'a> Widget for Map<'a> { // Drag represents offset of view from the player_pos in chunk coords let drag_new = drag + dragged / zoom; if drag_new != drag { - events.push(Event::SettingsChange(MapDrag(drag_new))); + events.push(Event::MapDrag(drag_new)); } }; @@ -748,37 +751,46 @@ impl<'a> Widget for Map<'a> { }); } - let wpos_to_rpos = |wpos: Vec2| { - // Site pos in world coordinates relative to the player - let rwpos = wpos - player_pos; - // Convert to chunk coordinates - let rcpos = rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32) + let wpos_to_rpos_fade = + |wpos: Vec2, bounding_rect_size: Vec2, fade_start: f32| { + // Site pos in world coordinates relative to the player + let rwpos = wpos - player_pos; + // Convert to chunk coordinates + let rcpos = rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32) // Add map dragging + drag.map(|e| e as f32); - // Convert to relative pixel coordinates from the center of the map - // Accounting for zooming - let rpos = rcpos.map(|e| e * zoom as f32); + // Convert to relative pixel coordinates from the center of the map + // Accounting for zooming + let rpos = rcpos.map(|e| e * zoom as f32); - if rpos - .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0) - .reduce_or() - { - None - } else { - Some(rpos) - } - }; + let dist_to_closest_map_edge = + (rpos.map2(map_size, |e, sz| sz as f32 / 2.0 - e.abs()) - bounding_rect_size) + .reduce_partial_min(); + match dist_to_closest_map_edge { + x if x <= 0.0 => None, + x if x < fade_start => Some(( + rpos, + // Easing function + 1.0 - 2.0_f32.powf(-10.0 * x / fade_start), + )), + _ => Some((rpos, 1.0)), + } + }; for (i, site_rich) in self.client.sites().values().enumerate() { let site = &site_rich.site; - let rpos = match wpos_to_rpos(site.wpos.map(|e| e as f32)) { + let rside = zoom as f32 * 8.0 * 1.2; + + let (rpos, fade) = match wpos_to_rpos_fade( + site.wpos.map(|e| e as f32), + Vec2::from(rside / 2.0), + rside / 2.0, + ) { Some(rpos) => rpos, None => continue, }; - let rside = zoom * 8.0; - let title = site.name.as_deref().unwrap_or_else(|| match &site.kind { SiteKind::Town => i18n.get("hud.map.town"), SiteKind::Dungeon { .. } => i18n.get("hud.map.dungeon"), @@ -810,7 +822,7 @@ impl<'a> Widget for Map<'a> { position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) - .w_h(rside * 1.2, rside * 1.2) + .w_h(rside as f64, rside as f64) .hover_image(match &site.kind { SiteKind::Town => self.imgs.mmap_site_town_hover, SiteKind::Dungeon { .. } => self.imgs.mmap_site_dungeon_hover, @@ -818,7 +830,7 @@ impl<'a> Widget for Map<'a> { SiteKind::Cave => self.imgs.mmap_site_cave_hover, SiteKind::Tree => self.imgs.mmap_site_tree_hover, }) - .image_color(UI_HIGHLIGHT_0) + .image_color(UI_HIGHLIGHT_0.alpha(fade)) .with_tooltip( self.tooltip_manager, title, @@ -942,7 +954,13 @@ impl<'a> Widget for Map<'a> { } } for (i, poi) in self.client.pois().iter().enumerate() { - let rpos = match wpos_to_rpos(poi.wpos.map(|e| e as f32)) { + // TODO: computation of text size to pass to wpos_to_rpos_fade, so it can + // determine when it's going past the edge of the map screen + let (rpos, fade) = match wpos_to_rpos_fade( + poi.wpos.map(|e| e as f32), + Vec2::from(zoom as f32 * 3.0), + zoom as f32 * 5.0, + ) { Some(rpos) => rpos, None => continue, }; @@ -960,14 +978,14 @@ impl<'a> Widget for Map<'a> { .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32)) .font_id(self.fonts.cyri.conrod_id) .graphics_for(state.ids.map_layers[0]) - .color(TEXT_BG) + .color(TEXT_BG.alpha(fade)) .set(state.ids.mmap_poi_title_bgs[i], ui); Text::new(title) .bottom_left_with_margins_on(state.ids.mmap_poi_title_bgs[i], 1.0, 1.0) .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32)) .font_id(self.fonts.cyri.conrod_id) //.graphics_for(state.ids.map_layers[0]) - .color(TEXT_COLOR) + .color(TEXT_COLOR.alpha(fade)) .set(state.ids.mmap_poi_titles[i], ui); handle_widget_mouse_events( @@ -992,14 +1010,14 @@ impl<'a> Widget for Map<'a> { .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32)) .font_id(self.fonts.cyri.conrod_id) .graphics_for(state.ids.map_layers[0]) - .color(TEXT_BG) + .color(TEXT_BG.alpha(fade)) .set(state.ids.peaks_txt_bg, ui); Text::new(&height) .bottom_left_with_margins_on(state.ids.peaks_txt_bg, 1.0, 1.0) .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32)) .font_id(self.fonts.cyri.conrod_id) .graphics_for(state.ids.map_layers[0]) - .color(TEXT_COLOR) + .color(TEXT_COLOR.alpha(fade)) .set(state.ids.peaks_txt, ui); } } @@ -1028,7 +1046,7 @@ impl<'a> Widget for Map<'a> { ) .font_id(self.fonts.cyri.conrod_id) .graphics_for(state.ids.map_layers[0]) - .color(TEXT_BLUE_COLOR) + .color(TEXT_BLUE_COLOR.alpha(fade)) .set(state.ids.mmap_poi_icons[i], ui); } }, @@ -1070,12 +1088,18 @@ impl<'a> Widget for Map<'a> { }; if let Some(member_pos) = member_pos { - let rpos = match wpos_to_rpos(member_pos.0.xy().map(|e| e as f32)) { - Some(rpos) => rpos, + let factor = 1.2; + let side_length = 20.0 * factor; + + let (rpos, fade) = match wpos_to_rpos_fade( + member_pos.0.xy().map(|e| e as f32), + Vec2::from(side_length / 2.0), + side_length / 2.0, + ) { + Some(x) => x, None => continue, }; - let factor = 1.2; let z_comparison = (member_pos.0.z - player_pos.z) as i32; Button::image(match z_comparison { @@ -1088,7 +1112,8 @@ impl<'a> Widget for Map<'a> { position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) - .w_h(20.0 * factor, 20.0 * factor) + .w_h(side_length as f64, side_length as f64) + .image_color(Color::Rgba(1.0, 1.0, 1.0, fade)) .floating(true) .with_tooltip(self.tooltip_manager, &name, "", &site_tooltip, TEXT_COLOR) .set(state.ids.member_indicators[i], ui); @@ -1105,19 +1130,23 @@ impl<'a> Widget for Map<'a> { // Location marker if self.show.map_marker { - if let Some((lm, rpos)) = self - .location_marker - .and_then(|lm| Some(lm).zip(wpos_to_rpos(lm))) - { - let factor = 1.4; - + let factor = 1.4; + let side_length = 20.0 * factor; + if let Some((lm, (rpos, fade))) = self.location_marker.and_then(|lm| { + Some(lm).zip(wpos_to_rpos_fade( + lm, + Vec2::from(side_length / 2.0), + side_length / 2.0, + )) + }) { if Button::image(self.imgs.location_marker) .x_y_position_relative_to( state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), - position::Relative::Scalar(rpos.y as f64 + 10.0 * factor), + position::Relative::Scalar(rpos.y as f64 + 10.0 * factor as f64), ) - .w_h(20.0 * factor, 20.0 * factor) + .w_h(side_length as f64, side_length as f64) + .image_color(Color::Rgba(1.0, 1.0, 1.0, fade)) .floating(true) .with_tooltip( self.tooltip_manager, @@ -1151,21 +1180,14 @@ impl<'a> Widget for Map<'a> { // Cursor stops moving on an axis as soon as it's position exceeds the maximum // // size of the widget - // Offset from map center due to dragging - let rcpos = drag.map(|e| e as f32); - // Convert to relative pixel coordinates from the center of the map - // Accounting for zooming - let rpos = rcpos.map(|e| e * zoom as f32); // Don't show if outside or near the edge of the map let arrow_sz = { let scale = 0.5; Vec2::new(36.0, 37.0) * scale }; // Hide if icon could go off of the edge of the map - let arrow_mag = arrow_sz.map(|e| e as f32 / 2.0).magnitude(); - if !rpos - .map2(map_size, |e, sz| e.abs() + arrow_mag > sz as f32 / 2.0) - .reduce_or() + if let Some((rpos, fade)) = + wpos_to_rpos_fade(player_pos.xy(), arrow_sz, arrow_sz.reduce_partial_min()) { Image::new(self.rot_imgs.indicator_mmap_small.target_north) .x_y_position_relative_to( @@ -1173,8 +1195,8 @@ impl<'a> Widget for Map<'a> { position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.y as f64), ) - .w_h(arrow_sz.x, arrow_sz.y) - .color(Some(UI_HIGHLIGHT_0)) + .w_h(arrow_sz.x as f64, arrow_sz.y as f64) + .color(Some(UI_HIGHLIGHT_0.alpha(fade))) .set(state.ids.indicator, ui); handle_widget_mouse_events( @@ -1219,7 +1241,7 @@ impl<'a> Widget for Map<'a> { .set(state.ids.recenter_button, ui) .was_clicked() { - events.push(Event::SettingsChange(MapDrag(Vec2::zero()))); + events.push(Event::MapDrag(Vec2::zero())); }; Image::new(self.imgs.m_move_ico) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a655b5bb7e..530756732c 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -930,6 +930,7 @@ pub struct Hud { crosshair_opacity: f32, floaters: Floaters, voxel_minimap: VoxelMinimap, + map_drag: Vec2, } impl Hud { @@ -1052,6 +1053,7 @@ impl Hud { combo_floaters: VecDeque::new(), block_floaters: Vec::new(), }, + map_drag: Vec2::zero(), } } @@ -3064,6 +3066,7 @@ impl Hud { global_state, tooltip_manager, self.show.location_marker, + self.map_drag, ) .set(self.ids.map, ui_widgets) { @@ -3082,6 +3085,9 @@ impl Hud { map::Event::SetLocationMarker(pos) => { self.show.location_marker = Some(pos); }, + map::Event::MapDrag(new_drag) => { + self.map_drag = new_drag; + }, map::Event::ToggleMarker => { self.show.map_marker = !self.show.map_marker; }, @@ -3089,12 +3095,7 @@ impl Hud { } } else { // Reset the map position when it's not showing - let drag = &global_state.settings.interface.map_drag; - if drag.x != 0.0 || drag.y != 0.0 { - events.push(Event::SettingsChange( - InterfaceChange::MapDrag(Vec2::zero()).into(), - )) - } + self.map_drag = Vec2::zero(); } if self.show.esc_menu { diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index d76f667876..11c215cd4b 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -15,7 +15,6 @@ use crate::{ GlobalState, }; use i18n::{LanguageMetadata, LocalizationHandle}; -use vek::*; #[derive(Clone)] pub enum Audio { @@ -118,7 +117,6 @@ pub enum Interface { MinimapZoom(f64), //Map settings MapZoom(f64), - MapDrag(Vec2), MapShowTopoMap(bool), MapShowDifficulty(bool), MapShowTowns(bool), @@ -497,9 +495,6 @@ impl SettingsChange { Interface::MapZoom(map_zoom) => { settings.interface.map_zoom = map_zoom; }, - Interface::MapDrag(map_drag) => { - settings.interface.map_drag = map_drag; - }, Interface::MapShowTopoMap(map_show_topo_map) => { settings.interface.map_show_topo_map = map_show_topo_map; }, diff --git a/voxygen/src/settings/interface.rs b/voxygen/src/settings/interface.rs index 1756186893..7618ba0f46 100644 --- a/voxygen/src/settings/interface.rs +++ b/voxygen/src/settings/interface.rs @@ -3,7 +3,6 @@ use crate::{ ui::ScaleMode, }; use serde::{Deserialize, Serialize}; -use vek::*; /// `InterfaceSettings` contains UI, HUD and Map options. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -29,7 +28,6 @@ pub struct InterfaceSettings { pub always_show_bars: bool, pub ui_scale: ScaleMode, pub map_zoom: f64, - pub map_drag: Vec2, pub map_show_topo_map: bool, pub map_show_difficulty: bool, pub map_show_towns: bool, @@ -68,7 +66,6 @@ impl Default for InterfaceSettings { always_show_bars: false, ui_scale: ScaleMode::RelativeToWindow([1920.0, 1080.0].into()), map_zoom: 10.0, - map_drag: Vec2 { x: 0.0, y: 0.0 }, map_show_topo_map: true, map_show_difficulty: true, map_show_towns: true,