From d0953fcab08c52310b7446a0107e995ed2f34129 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Sat, 1 May 2021 16:26:13 +0000 Subject: [PATCH] Added map marker --- CHANGELOG.md | 1 + .../element/ui/generic/icons/m_click.png | 3 + .../element/ui/generic/icons/m_scroll.png | 4 +- .../ui/map/buttons/location_marker.png | 3 + assets/voxygen/i18n/en/hud/map.ron | 3 + voxygen/src/hud/img_ids.rs | 2 + voxygen/src/hud/map.rs | 133 +++++++++++++----- voxygen/src/hud/minimap.rs | 71 ++++++---- voxygen/src/hud/mod.rs | 15 ++ 9 files changed, 177 insertions(+), 58 deletions(-) create mode 100644 assets/voxygen/element/ui/generic/icons/m_click.png mode change 100644 => 100755 assets/voxygen/element/ui/generic/icons/m_scroll.png create mode 100755 assets/voxygen/element/ui/map/buttons/location_marker.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a5cc791b..47658a2714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - You can now block and parry with melee weapons - Lift is now calculated for gliders based on dimensions (currently same for all) - Specific music tracks can now play exclusively in towns. +- Custom map markers can be placed now ### Changed diff --git a/assets/voxygen/element/ui/generic/icons/m_click.png b/assets/voxygen/element/ui/generic/icons/m_click.png new file mode 100644 index 0000000000..1b61e1a964 --- /dev/null +++ b/assets/voxygen/element/ui/generic/icons/m_click.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a245ef721164a195b467a74a71eead180c7fcab2a3b2e2f5d6367683e642598 +size 257 diff --git a/assets/voxygen/element/ui/generic/icons/m_scroll.png b/assets/voxygen/element/ui/generic/icons/m_scroll.png old mode 100644 new mode 100755 index 3a4c5c74a6..56edb7654e --- a/assets/voxygen/element/ui/generic/icons/m_scroll.png +++ b/assets/voxygen/element/ui/generic/icons/m_scroll.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cd0b1a9b66602221fa63bffad5b182dfb87619340d2f0bfa1921b69801e720df -size 163 +oid sha256:456a162985e013e86060153c1fbf4826f383a3d0cf51c8ab2d0dc216a4297358 +size 238 diff --git a/assets/voxygen/element/ui/map/buttons/location_marker.png b/assets/voxygen/element/ui/map/buttons/location_marker.png new file mode 100755 index 0000000000..c64e38a749 --- /dev/null +++ b/assets/voxygen/element/ui/map/buttons/location_marker.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:012ff57c867d39b5b12b5ae8cc3cb2cbb58a1be3ff2a7192ec25aa8a57257b97 +size 731 diff --git a/assets/voxygen/i18n/en/hud/map.ron b/assets/voxygen/i18n/en/hud/map.ron index 4fbf02bc25..d0ccb38cbb 100644 --- a/assets/voxygen/i18n/en/hud/map.ron +++ b/assets/voxygen/i18n/en/hud/map.ron @@ -21,7 +21,10 @@ "hud.map.difficulty_dungeon": "Dungeon\n\nDifficulty: {difficulty}", "hud.map.drag": "Drag", "hud.map.zoom": "Zoom", + "hud.map.mid_click": "Set Waypoint", "hud.map.recenter": "Recenter", + "hud.map.marked_location": "Marked Location", + "hud.map.marked_location_remove": "Click to remove", }, diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 39d28d18f6..8a47d6eb1e 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -308,6 +308,7 @@ image_ids! { m2_ico: "voxygen.element.ui.generic.icons.m2", m_scroll_ico: "voxygen.element.ui.generic.icons.m_scroll", m_move_ico: "voxygen.element.ui.generic.icons.m_move", + m_click_ico: "voxygen.element.ui.generic.icons.m_click", skillbar_slot: "voxygen.element.ui.skillbar.slot", // Other Icons/Art @@ -337,6 +338,7 @@ image_ids! { indicator_group: "voxygen.element.ui.map.buttons.group_indicator", indicator_group_up: "voxygen.element.ui.map.buttons.group_indicator_arrow_up", indicator_group_down: "voxygen.element.ui.map.buttons.group_indicator_arrow_down", + location_marker: "voxygen.element.ui.map.buttons.location_marker", map_mode_overlay: "voxygen.element.ui.map.buttons.map_modes", // MiniMap diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index c49bdc4dc3..ec02f3a33f 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -1,7 +1,7 @@ use super::{ img_ids::{Imgs, ImgsRot}, - QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, - TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, + Show, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, + TEXT_COLOR, TEXT_GRAY_COLOR, TEXT_VELORITE, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::{ i18n::Localization, @@ -40,6 +40,7 @@ widget_ids! { site_difs[], member_indicators[], member_height_indicators[], + location_marker, map_settings_align, show_towns_img, show_towns_box, @@ -64,6 +65,8 @@ widget_ids! { drag_ico, zoom_txt, zoom_ico, + waypoint_ico, + waypoint_txt, map_mode_btn, map_mode_overlay, } @@ -73,6 +76,7 @@ const SHOW_ECONOMY: bool = false; // turn this display off (for 0.9) until we ha #[derive(WidgetCommon)] pub struct Map<'a> { + show: &'a Show, client: &'a Client, world_map: &'a (Vec, Vec2), imgs: &'a Imgs, @@ -84,10 +88,12 @@ pub struct Map<'a> { global_state: &'a GlobalState, rot_imgs: &'a ImgsRot, tooltip_manager: &'a mut TooltipManager, + location_marker: Option>, } impl<'a> Map<'a> { #[allow(clippy::too_many_arguments)] // TODO: Pending review in #587 pub fn new( + show: &'a Show, client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, @@ -97,8 +103,10 @@ impl<'a> Map<'a> { localized_strings: &'a Localization, global_state: &'a GlobalState, tooltip_manager: &'a mut TooltipManager, + location_marker: Option>, ) -> Self { Self { + show, imgs, rot_imgs, world_map, @@ -109,6 +117,7 @@ impl<'a> Map<'a> { localized_strings, global_state, tooltip_manager, + location_marker, } } } @@ -121,6 +130,8 @@ pub enum Event { SettingsChange(InterfaceChange), Close, RequestSiteInfo(SiteId), + SetLocationMarker(Vec2), + ToggleMarker, } fn get_site_economy(site_rich: &SiteInfoRich) -> String { @@ -273,12 +284,6 @@ impl<'a> Widget for Map<'a> { }); } - Image::new(self.imgs.map_frame_art) - .mid_top_with_margin_on(state.ids.map_align, 5.0) - .w_h(765.0, 765.0) - .parent(state.ids.bg) - .set(state.ids.map_layers[0], ui); - // Map Size let worldsize = self.world_map.1; @@ -298,6 +303,7 @@ impl<'a> Widget for Map<'a> { let w_src = max_zoom / zoom; let h_src = max_zoom / zoom; + // Handle dragging let drag = self.global_state.settings.interface.map_drag; let dragged: Vec2 = ui @@ -318,6 +324,20 @@ impl<'a> Widget for Map<'a> { ], [w_src, h_src], ); + // Handle Location Marking + if let Some(click) = ui + .widget_input(state.ids.map_layers[0]) + .clicks() + .middle() + .next() + { + events.push(Event::SetLocationMarker( + (Vec2::::from(click.xy) / map_size / zoom * max_zoom - drag) + .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as f32 * sz as f32) + + player_pos, + )); + events.push(Event::ToggleMarker); + } // X-Button if Button::image(self.imgs.close_button) .w_h(24.0, 25.0) @@ -333,14 +353,14 @@ impl<'a> Widget for Map<'a> { // Map Layer Images for (index, layer) in self.world_map.0.iter().enumerate() { if index == 0 { - Image::new(layer.none) + Button::image(layer.none) .mid_top_with_margin_on(state.ids.map_align, 10.0) .w_h(map_size.x, map_size.y) .parent(state.ids.bg) .source_rectangle(rect_src) .set(state.ids.map_layers[index], ui); } else if show_topo_map { - Image::new(layer.none) + Button::image(layer.none) .mid_top_with_margin_on(state.ids.map_align, 10.0) .w_h(map_size.x, map_size.y) .parent(state.ids.bg) @@ -587,10 +607,10 @@ impl<'a> Widget for Map<'a> { .resize(self.client.sites().len(), &mut ui.widget_id_generator()) }); } - for (i, site_rich) in self.client.sites().values().enumerate() { - let site = &site_rich.site; + + let wpos_to_rpos = |wpos: Vec2| { // Site pos in world coordinates relative to the player - let rwpos = site.wpos.map(|e| e as f32) - player_pos; + 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 @@ -600,14 +620,27 @@ impl<'a> Widget for Map<'a> { // Convert to relative pixel coordinates from the center of the map // Accounting for zooming let rpos = rfpos.map2(map_size, |e, sz| e * sz as f32 * zoom as f32); - let rside = zoom * 6.0; if rpos .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0) .reduce_or() { - continue; + None + } else { + Some(rpos) } + }; + + 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)) { + Some(rpos) => rpos, + None => continue, + }; + + let rside = zoom * 6.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"), @@ -783,24 +816,11 @@ impl<'a> Widget for Map<'a> { }; if let Some(member_pos) = member_pos { - // Site pos in world coordinates relative to the player - let rwpos = member_pos.0.xy().map(|e| e as f32) - 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 fractional coordinates relative to the worldsize - let rfpos = rcpos / max_zoom as f32; - // Convert to relative pixel coordinates from the center of the map - // Accounting for zooming - let rpos = rfpos.map2(map_size, |e, sz| e * sz as f32 * zoom as f32); + let rpos = match wpos_to_rpos(member_pos.0.xy().map(|e| e as f32)) { + Some(rpos) => rpos, + None => continue, + }; - if rpos - .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0) - .reduce_or() - { - continue; - } let factor = 1.2; let z_comparison = (member_pos.0.z - player_pos.z) as i32; @@ -820,6 +840,43 @@ impl<'a> Widget for Map<'a> { .set(state.ids.member_indicators[i], ui); } } + + // 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; + + 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), + ) + .w_h(20.0 * factor, 20.0 * factor) + .floating(true) + .with_tooltip( + self.tooltip_manager, + i18n.get("hud.map.marked_location"), + &format!( + "X: {}, Y: {}\n\n{}", + lm.x as i32, + lm.y as i32, + i18n.get("hud.map.marked_location_remove") + ), + &site_tooltip, + TEXT_VELORITE, + ) + .set(state.ids.location_marker, ui) + .was_clicked() + { + events.push(Event::ToggleMarker); + } + } + } + // Cursor pos relative to playerpos and widget size // Cursor stops moving on an axis as soon as it's position exceeds the maximum // // size of the widget @@ -918,6 +975,18 @@ impl<'a> Widget for Map<'a> { .graphics_for(state.ids.map_layers[0]) .color(TEXT_COLOR) .set(state.ids.zoom_txt, ui); + Image::new(self.imgs.m_click_ico) + .right_from(state.ids.zoom_txt, 5.0) + .w_h(icon_size.x, icon_size.y) + .color(Some(UI_HIGHLIGHT_0)) + .set(state.ids.waypoint_ico, ui); + Text::new(i18n.get("hud.map.mid_click")) + .right_from(state.ids.waypoint_ico, 5.0) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .graphics_for(state.ids.map_layers[0]) + .color(TEXT_COLOR) + .set(state.ids.waypoint_txt, ui); // Show topographic map if Button::image(self.imgs.button) diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index 20fa060b02..0fd79bdca8 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -1,6 +1,6 @@ use super::{ img_ids::{Imgs, ImgsRot}, - QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, + Show, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::{ @@ -39,13 +39,14 @@ widget_ids! { mmap_site_icons_bgs[], mmap_site_icons[], member_indicators[], + location_marker, } } #[derive(WidgetCommon)] pub struct MiniMap<'a> { + show: &'a Show, client: &'a Client, - imgs: &'a Imgs, rot_imgs: &'a ImgsRot, world_map: &'a (Vec, Vec2), @@ -54,10 +55,12 @@ pub struct MiniMap<'a> { common: widget::CommonBuilder, ori: Vec3, global_state: &'a GlobalState, + location_marker: Option>, } impl<'a> MiniMap<'a> { pub fn new( + show: &'a Show, client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, @@ -65,8 +68,10 @@ impl<'a> MiniMap<'a> { fonts: &'a Fonts, ori: Vec3, global_state: &'a GlobalState, + location_marker: Option>, ) -> Self { Self { + show, client, imgs, rot_imgs, @@ -75,6 +80,7 @@ impl<'a> MiniMap<'a> { common: widget::CommonBuilder::default(), ori, global_state, + location_marker, } } } @@ -289,10 +295,10 @@ impl<'a> Widget for MiniMap<'a> { .resize(self.client.sites().len(), &mut ui.widget_id_generator()) }); } - for (i, site_rich) in self.client.sites().values().enumerate() { - let site = &site_rich.site; + + let wpos_to_rpos = |wpos: Vec2, limit: bool| { // Site pos in world coordinates relative to the player - let rwpos = site.wpos.map(|e| e as f32) - player_pos; + let rwpos = wpos - player_pos; // Convert to chunk coordinates let rcpos = rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32); // Convert to fractional coordinates relative to the worldsize @@ -308,8 +314,22 @@ impl<'a> Widget for MiniMap<'a> { .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0) .reduce_or() { - continue; + limit.then(|| { + let clamped = rpos / rpos.map(|e| e.abs()).reduce_partial_max(); + clamped * map_size.map(|e| e as f32) / 2.0 + }) + } else { + Some(rpos) } + }; + + 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), false) { + Some(rpos) => rpos, + None => continue, + }; Image::new(match &site.kind { SiteKind::Town => self.imgs.mmap_site_town_bg, @@ -382,25 +402,11 @@ impl<'a> Widget for MiniMap<'a> { let member_pos = entity.and_then(|entity| member_pos.get(entity)); if let Some(member_pos) = member_pos { - // Site pos in world coordinates relative to the player - let rwpos = member_pos.0.xy().map(|e| e as f32) - player_pos; - // Convert to chunk coordinates - let rcpos = rwpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e / sz as f32); - // Convert to fractional coordinates relative to the worldsize - let rfpos = rcpos / max_zoom as f32; - // Convert to unrotated pixel coordinates from the player location on the map - // (the center) - // Accounting for zooming - let rpixpos = rfpos.map2(map_size, |e, sz| e * sz as f32 * zoom as f32); - let rpos = Vec2::unit_x().rotated_z(orientation.x) * rpixpos.x - + Vec2::unit_y().rotated_z(orientation.x) * rpixpos.y; + let rpos = match wpos_to_rpos(member_pos.0.xy().map(|e| e as f32), false) { + Some(rpos) => rpos, + None => continue, + }; - if rpos - .map2(map_size, |e, sz| e.abs() > sz as f32 / 2.0) - .reduce_or() - { - continue; - } let factor = 1.2; let z_comparison = (member_pos.0.z - player_pos.z) as i32; Button::image(match z_comparison { @@ -419,6 +425,23 @@ impl<'a> Widget for MiniMap<'a> { } } + // Location marker + if self.show.map_marker { + if let Some(rpos) = self.location_marker.and_then(|lm| wpos_to_rpos(lm, true)) { + let factor = 1.2; + + 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 + 8.0 * factor), + ) + .w_h(16.0 * factor, 16.0 * factor) + //.image_color(Color::Rgba(1.0, 1.0, 1.0, 1.0)) + .floating(true) + .set(state.ids.location_marker, ui); + } + } // Indicator let ind_scale = 0.4; let ind_rotation = if is_facing_north { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index b2f6d6265f..795416fa22 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -104,6 +104,7 @@ use std::{ use vek::*; const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); +const TEXT_VELORITE: Color = Color::Rgba(0.0, 0.66, 0.66, 1.0); const TEXT_GRAY_COLOR: Color = Color::Rgba(0.5, 0.5, 0.5, 1.0); const TEXT_DULL_RED_COLOR: Color = Color::Rgba(0.56, 0.2, 0.2, 1.0); const TEXT_BG: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0); @@ -511,6 +512,8 @@ pub struct Show { auto_walk: bool, camera_clamp: bool, prompt_dialog: Option, + location_marker: Option>, + map_marker: bool, } impl Show { fn bag(&mut self, open: bool) { @@ -876,6 +879,8 @@ impl Hud { auto_walk: false, camera_clamp: false, prompt_dialog: None, + location_marker: None, + map_marker: false, }, to_focus: None, //never_show: false, @@ -2289,6 +2294,7 @@ impl Hud { // MiniMap for event in MiniMap::new( + &self.show, client, &self.imgs, &self.rot_imgs, @@ -2296,6 +2302,7 @@ impl Hud { &self.fonts, camera.get_orientation(), &global_state, + self.show.location_marker, ) .set(self.ids.minimap, ui_widgets) { @@ -2725,6 +2732,7 @@ impl Hud { // Map if self.show.map { for event in Map::new( + &self.show, client, &self.imgs, &self.rot_imgs, @@ -2734,6 +2742,7 @@ impl Hud { i18n, &global_state, tooltip_manager, + self.show.location_marker, ) .set(self.ids.map, ui_widgets) { @@ -2749,6 +2758,12 @@ impl Hud { map::Event::RequestSiteInfo(id) => { events.push(Event::RequestSiteInfo(id)); }, + map::Event::SetLocationMarker(pos) => { + self.show.location_marker = Some(pos); + }, + map::Event::ToggleMarker => { + self.show.map_marker = !self.show.map_marker; + }, } } } else {