1use super::{
2 MapMarkers, QUALITY_COMMON, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, TEXT_BG,
3 TEXT_BLUE_COLOR, TEXT_COLOR, TEXT_GRAY_COLOR, TEXT_VELORITE, UI_HIGHLIGHT_0, UI_MAIN,
4 img_ids::{Imgs, ImgsRot},
5};
6use crate::{
7 GlobalState,
8 game_input::GameInput,
9 session::settings_change::{Interface as InterfaceChange, Interface::*},
10 ui::{ImageFrame, Tooltip, TooltipManager, Tooltipable, fonts::Fonts, img_ids},
11 window::KeyMouse,
12};
13use client::{self, Client, SiteMarker};
14use common::{
15 comp,
16 comp::group::Role,
17 map::{Marker, MarkerFlags, MarkerKind},
18 terrain::{CoordinateConversions, TerrainChunkSize},
19 trade::Good,
20 vol::RectVolSize,
21};
22use common_net::msg::world_msg::{PoiKind, SiteId};
23use conrod_core::{
24 Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, color,
25 input::MouseButton as ConrodMouseButton,
26 position,
27 widget::{self, Button, Image, Rectangle, Text},
28 widget_ids,
29};
30use i18n::Localization;
31use specs::WorldExt;
32use std::borrow::Cow;
33use vek::*;
34use winit::event::MouseButton;
35
36widget_ids! {
37 struct Ids {
38 frame,
39 bg,
40 icon,
41 close,
42 title,
43 map_align,
44 qlog_align,
45 location_name,
46 indicator,
47 indicator_overlay,
48 map_layers[],
49 map_title,
50 qlog_title,
51 zoom_slider,
52 mmap_site_icons[],
53 mmap_poi_icons[],
54 mmap_poi_title_bgs[],
55 mmap_poi_titles[],
56 peaks_txt,
57 peaks_txt_bg,
58 site_difs[],
59 member_indicators[],
60 member_height_indicators[],
61 location_marker,
62 location_marker_group[],
63 map_settings_align,
64 show_towns_img,
65 show_towns_box,
66 show_towns_text,
67 show_sea_chapels_img,
68 show_sea_chapels_box,
69 show_sea_chapels_text,
70 show_castles_img,
71 show_castles_box,
72 show_castles_text,
73 show_bridges_img,
74 show_bridges_box,
75 show_bridges_text,
76 show_dungeons_img,
77 show_dungeons_box,
78 show_dungeons_text,
79 show_caves_img,
80 show_caves_box,
81 show_caves_text,
82 show_trees_img,
83 show_trees_box,
84 show_trees_text,
85 show_peaks_img,
86 show_peaks_box,
87 show_peaks_text,
88 show_biomes_img,
89 show_biomes_box,
90 show_biomes_text,
91 show_glider_courses_img,
92 show_glider_courses_box,
93 show_glider_courses_text,
94 show_quests_img,
95 show_quests_box,
96 show_quests_text,
97 show_voxel_map_img,
98 show_voxel_map_box,
99 show_voxel_map_text,
100 show_difficulty_img,
101 show_difficulty_box,
102 show_difficulty_text,
103 recenter_button,
104 drag_txt,
105 drag_ico,
106 zoom_txt,
107 zoom_ico,
108 waypoint_binding_txt,
109 waypoint_txt,
110 map_mode_btn,
111 map_mode_overlay,
112 minimap_mode_btn,
113 minimap_mode_overlay,
114
115 }
116}
117
118const SHOW_ECONOMY: bool = false; pub struct ExtraMarker {
121 pub recv_pos: Vec2<f32>,
122 pub marker: Marker,
123}
124
125#[derive(WidgetCommon)]
126pub struct Map<'a> {
127 client: &'a Client,
128 world_map: &'a (Vec<img_ids::Rotations>, Vec2<u32>),
129 imgs: &'a Imgs,
130 fonts: &'a Fonts,
131 #[conrod(common_builder)]
132 common: widget::CommonBuilder,
133 pulse: f32,
134 localized_strings: &'a Localization,
135 global_state: &'a GlobalState,
136 rot_imgs: &'a ImgsRot,
137 tooltip_manager: &'a mut TooltipManager,
138 location_markers: &'a MapMarkers,
139 map_drag: Vec2<f64>,
140 extra_markers: &'a [ExtraMarker],
141}
142impl<'a> Map<'a> {
143 pub fn new(
144 client: &'a Client,
145 imgs: &'a Imgs,
146 rot_imgs: &'a ImgsRot,
147 world_map: &'a (Vec<img_ids::Rotations>, Vec2<u32>),
148 fonts: &'a Fonts,
149 pulse: f32,
150 localized_strings: &'a Localization,
151 global_state: &'a GlobalState,
152 tooltip_manager: &'a mut TooltipManager,
153 location_markers: &'a MapMarkers,
154 map_drag: Vec2<f64>,
155 extra_markers: &'a [ExtraMarker],
156 ) -> Self {
157 Self {
158 imgs,
159 rot_imgs,
160 world_map,
161 client,
162 fonts,
163 common: widget::CommonBuilder::default(),
164 pulse,
165 localized_strings,
166 global_state,
167 tooltip_manager,
168 location_markers,
169 map_drag,
170 extra_markers,
171 }
172 }
173}
174
175pub struct State {
176 ids: Ids,
177}
178
179pub enum Event {
180 SettingsChange(InterfaceChange),
181 Close,
182 RequestSiteInfo(SiteId),
183 SetLocationMarker(Vec2<i32>),
184 MapDrag(Vec2<f64>),
185 RemoveMarker,
186}
187
188fn get_site_economy(site: &SiteMarker) -> String {
189 if SHOW_ECONOMY {
190 if let Some(economy) = &site.economy {
191 use common::trade::Good::{Armor, Coin, Food, Ingredients, Potions, Tools};
192 let mut result = format!("\n\nPopulation {:?}", economy.population);
193 result += "\nStock";
194 for i in [Food, Potions, Ingredients, Coin, Tools, Armor].iter() {
195 result += &format!("\n {:?}={:.3}", *i, *economy.stock.get(i).unwrap_or(&0.0));
196 }
197 result += "\nPrice";
198 for i in [Food, Potions, Ingredients, Coin, Tools, Armor].iter() {
199 result += &format!("\n {:?}={:.3}", *i, *economy.values.get(i).unwrap_or(&0.0));
200 }
201
202 let mut trade_sorted: Vec<(&Good, &f32)> = economy.last_exports.iter().collect();
203 trade_sorted.sort_unstable_by(|a, b| a.1.partial_cmp(b.1).unwrap());
204 if !trade_sorted.is_empty() {
205 result += &format!("\nTrade {:.1} ", *(trade_sorted.first().unwrap().1));
206 for i in trade_sorted.iter().filter(|x| *x.1 != 0.0) {
207 result += &format!("{:?} ", i.0);
208 }
209 result += &format!("{:.3}", *(trade_sorted.last().unwrap().1));
210 }
211 result
212 } else {
213 format!("\nloading economy for\n{:?}", site.marker.site)
214 }
215 } else {
216 "".into()
217 }
218}
219
220impl From<&KeyMouse> for ConrodMouseButton {
221 fn from(key: &KeyMouse) -> Self {
222 match key {
223 KeyMouse::Mouse(MouseButton::Left) => ConrodMouseButton::Left,
224 KeyMouse::Mouse(MouseButton::Right) => ConrodMouseButton::Right,
225 KeyMouse::Mouse(MouseButton::Middle) => ConrodMouseButton::Middle,
226 KeyMouse::Mouse(MouseButton::Other(0)) => ConrodMouseButton::X1,
227 KeyMouse::Mouse(MouseButton::Other(1)) => ConrodMouseButton::X2,
228 KeyMouse::Mouse(MouseButton::Other(2)) => ConrodMouseButton::Button6,
229 KeyMouse::Mouse(MouseButton::Other(3)) => ConrodMouseButton::Button7,
230 KeyMouse::Mouse(MouseButton::Other(4)) => ConrodMouseButton::Button8,
231 _ => conrod_core::input::MouseButton::Unknown,
232 }
233 }
234}
235
236impl Widget for Map<'_> {
237 type Event = Vec<Event>;
238 type State = State;
239 type Style = ();
240
241 fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
242 State {
243 ids: Ids::new(id_gen),
244 }
245 }
246
247 fn style(&self) -> Self::Style {}
248
249 #[expect(clippy::neg_multiply)]
250 fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
251 common_base::prof_span!("Map::update");
252 let widget::UpdateArgs { state, ui, .. } = args;
253 let colored_player_marker = self
254 .global_state
255 .settings
256 .interface
257 .minimap_colored_player_marker;
258 let zoom = self.global_state.settings.interface.map_zoom;
259 let show_difficulty = self.global_state.settings.interface.map_show_difficulty;
260 let show_towns = self.global_state.settings.interface.map_show_towns;
261 let show_dungeons = self.global_state.settings.interface.map_show_dungeons;
262 let show_castles = self.global_state.settings.interface.map_show_castles;
263 let show_bridges = self.global_state.settings.interface.map_show_bridges;
264 let show_caves = self.global_state.settings.interface.map_show_caves;
265 let show_trees = self.global_state.settings.interface.map_show_trees;
266 let show_peaks = self.global_state.settings.interface.map_show_peaks;
267 let show_biomes = self.global_state.settings.interface.map_show_biomes;
268 let show_glider_courses = self.global_state.settings.interface.map_show_glider_courses;
269 let show_quests = self.global_state.settings.interface.map_show_quests;
270 let show_voxel_map = self.global_state.settings.interface.map_show_voxel_map;
271 let show_topo_map = self.global_state.settings.interface.map_show_topo_map;
272 let location_marker_binding = self
273 .global_state
274 .settings
275 .controls
276 .keybindings
277 .get(&GameInput::MapSetMarker)
278 .cloned()
279 .flatten()
280 .unwrap_or(KeyMouse::Mouse(MouseButton::Middle));
281 let mut events = Vec::new();
282 let i18n = &self.localized_strings;
283 let site_tooltip = Tooltip::new({
285 let edge = &self.rot_imgs.tt_side;
288 let corner = &self.rot_imgs.tt_corner;
289 ImageFrame::new(
290 [edge.cw180, edge.none, edge.cw270, edge.cw90],
291 [corner.none, corner.cw270, corner.cw90, corner.cw180],
292 Color::Rgba(0.08, 0.07, 0.04, 1.0),
293 5.0,
294 )
295 })
296 .title_font_size(self.fonts.cyri.scale(15))
297 .parent(ui.window)
298 .desc_font_size(self.fonts.cyri.scale(12))
299 .font_id(self.fonts.cyri.conrod_id)
300 .desc_text_color(TEXT_COLOR);
301 Image::new(self.imgs.map_bg)
303 .w_h(1202.0, 886.0)
304 .mid_top_with_margin_on(ui.window, 5.0)
305 .color(Some(UI_MAIN))
306 .set(state.ids.bg, ui);
307
308 Image::new(self.imgs.map_frame)
309 .w_h(1202.0, 886.0)
310 .middle_of(state.ids.bg)
311 .color(Some(UI_HIGHLIGHT_0))
312 .set(state.ids.frame, ui);
313
314 Rectangle::fill_with([814.0, 834.0], color::TRANSPARENT)
316 .top_left_with_margins_on(state.ids.frame, 46.0, 240.0)
317 .set(state.ids.map_align, ui);
318
319 Rectangle::fill_with([232.0, 814.0], color::TRANSPARENT)
321 .top_left_with_margins_on(state.ids.frame, 44.0, 2.0)
322 .set(state.ids.qlog_align, ui);
323
324 Image::new(self.imgs.map_icon)
326 .w_h(30.0, 30.0)
327 .top_left_with_margins_on(state.ids.frame, 6.0, 8.0)
328 .set(state.ids.icon, ui);
329
330 Text::new(&i18n.get_msg("hud-map-map_title"))
332 .mid_top_with_margin_on(state.ids.frame, 3.0)
333 .font_id(self.fonts.cyri.conrod_id)
334 .font_size(self.fonts.cyri.scale(29))
335 .color(TEXT_COLOR)
336 .set(state.ids.map_title, ui);
337
338 Text::new(&i18n.get_msg("hud-map-qlog_title"))
340 .mid_top_with_margin_on(state.ids.qlog_align, 6.0)
341 .font_id(self.fonts.cyri.conrod_id)
342 .font_size(self.fonts.cyri.scale(21))
343 .color(TEXT_COLOR)
344 .set(state.ids.qlog_title, ui);
345
346 if state.ids.map_layers.len() < self.world_map.0.len() {
365 state.update(|state| {
366 state
367 .ids
368 .map_layers
369 .resize(self.world_map.0.len(), &mut ui.widget_id_generator())
370 });
371 }
372
373 let worldsize = self.world_map.1;
375
376 let player_pos = self
378 .client
379 .state()
380 .ecs()
381 .read_storage::<comp::Pos>()
382 .get(self.client.entity())
383 .map_or(Vec3::zero(), |pos| pos.0);
384
385 let map_size = Vec2::new(760.0, 760.0);
386
387 let player_pos_chunks =
388 player_pos.xy().map(|x| x as f64) / TerrainChunkSize::RECT_SIZE.map(|x| x as f64);
389 let min_drag = player_pos_chunks - worldsize.map(|x| x as f64);
390 let max_drag = player_pos_chunks;
391 let drag = self.map_drag.clamped(min_drag, max_drag);
392
393 enum MarkerChange {
394 Pos(Vec2<f32>),
395 ClickPos,
396 Remove,
397 }
398
399 let handle_widget_mouse_events =
400 |widget, marker: MarkerChange, ui: &mut UiCell, events: &mut Vec<Event>, map_widget| {
401 if let Some(click) = ui
403 .widget_input(widget)
404 .clicks()
405 .button(ConrodMouseButton::from(&location_marker_binding))
406 .next()
407 {
408 match marker {
409 MarkerChange::Pos(ref wpos) => {
410 events.push(Event::SetLocationMarker(wpos.as_()))
411 },
412 MarkerChange::ClickPos => {
413 let tmp: Vec2<f64> = Vec2::<f64>::from(click.xy) / zoom - drag;
414 let wpos = tmp.as_::<f32>().cpos_to_wpos() + player_pos;
415 events.push(Event::SetLocationMarker(wpos.as_()));
416 },
417 MarkerChange::Remove => events.push(Event::RemoveMarker),
418 }
419 }
420
421 let scrolled: f64 = ui
423 .widget_input(widget)
424 .scrolls()
425 .map(|scroll| scroll.y)
426 .sum();
427 if scrolled != 0.0 {
428 let min_zoom = map_size.x / worldsize.reduce_partial_max() as f64 / 2.0;
429 let new_zoom_lvl: f64 = (f64::log2(zoom) - scrolled * 0.03)
430 .exp2()
431 .clamp(min_zoom, 16.0);
432 events.push(Event::SettingsChange(MapZoom(new_zoom_lvl)));
433 let cursor_mouse_pos = ui
434 .widget_input(map_widget)
435 .mouse()
436 .map(|mouse| mouse.rel_xy());
437 if let Some(cursor_pos) = cursor_mouse_pos {
438 let mouse_pos = Vec2::from_slice(&cursor_pos);
439 let drag_new = drag + mouse_pos * (1.0 / new_zoom_lvl - 1.0 / zoom);
440 if drag_new != drag {
441 events.push(Event::MapDrag(drag_new));
442 }
443 }
444 }
445
446 let dragged: Vec2<f64> = ui
448 .widget_input(widget)
449 .drags()
450 .left()
451 .map(|drag| Vec2::<f64>::from(drag.delta_xy))
452 .sum();
453 let drag_new = drag + dragged / zoom;
455 if drag_new != drag {
456 events.push(Event::MapDrag(drag_new));
457 }
458 };
459
460 handle_widget_mouse_events(
461 state.ids.map_layers[0],
462 MarkerChange::ClickPos,
463 ui,
464 &mut events,
465 state.ids.map_layers[0],
466 );
467
468 let rect_src = position::Rect::from_xy_dim(
469 [
470 (player_pos.x as f64 / TerrainChunkSize::RECT_SIZE.x as f64) - drag.x,
471 (worldsize.y as f64 - (player_pos.y as f64 / TerrainChunkSize::RECT_SIZE.y as f64))
472 + drag.y,
473 ],
474 [map_size.x / zoom, map_size.y / zoom],
475 );
476
477 if Button::image(self.imgs.close_button)
479 .w_h(24.0, 25.0)
480 .hover_image(self.imgs.close_btn_hover)
481 .press_image(self.imgs.close_btn_press)
482 .top_right_with_margins_on(state.ids.frame, 0.0, 0.0)
483 .set(state.ids.close, ui)
484 .was_clicked()
485 {
486 events.push(Event::Close);
487 }
488
489 for (index, layer) in self.world_map.0.iter().enumerate() {
491 if index == 0 {
492 Button::image(layer.none)
493 .mid_top_with_margin_on(state.ids.map_align, 10.0)
494 .w_h(map_size.x, map_size.y)
495 .parent(state.ids.bg)
496 .source_rectangle(rect_src)
497 .set(state.ids.map_layers[index], ui);
498 } else if show_topo_map {
499 Button::image(layer.none)
500 .mid_top_with_margin_on(state.ids.map_align, 10.0)
501 .w_h(map_size.x, map_size.y)
502 .parent(state.ids.bg)
503 .source_rectangle(rect_src)
504 .graphics_for(state.ids.map_layers[0])
505 .set(state.ids.map_layers[index], ui);
506 }
507 }
508
509 Rectangle::fill_with([150.0, 200.0], color::TRANSPARENT)
512 .top_right_with_margins_on(state.ids.frame, 55.0, 10.0)
513 .set(state.ids.map_settings_align, ui);
514 Image::new(self.imgs.map_dif_icon)
517 .top_left_with_margins_on(state.ids.map_settings_align, 5.0, 5.0)
518 .w_h(20.0, 20.0)
519 .set(state.ids.show_difficulty_img, ui);
520 if Button::image(if show_difficulty {
521 self.imgs.checkbox_checked
522 } else {
523 self.imgs.checkbox
524 })
525 .w_h(18.0, 18.0)
526 .hover_image(if show_difficulty {
527 self.imgs.checkbox_checked_mo
528 } else {
529 self.imgs.checkbox_mo
530 })
531 .press_image(if show_difficulty {
532 self.imgs.checkbox_checked
533 } else {
534 self.imgs.checkbox_press
535 })
536 .right_from(state.ids.show_difficulty_img, 10.0)
537 .set(state.ids.show_difficulty_box, ui)
538 .was_clicked()
539 {
540 events.push(Event::SettingsChange(MapShowDifficulty(!show_difficulty)));
541 }
542 Text::new(&i18n.get_msg("hud-map-difficulty"))
543 .right_from(state.ids.show_difficulty_box, 10.0)
544 .font_size(self.fonts.cyri.scale(14))
545 .font_id(self.fonts.cyri.conrod_id)
546 .graphics_for(state.ids.show_difficulty_box)
547 .color(TEXT_COLOR)
548 .set(state.ids.show_difficulty_text, ui);
549 Image::new(self.imgs.mmap_site_town)
551 .down_from(state.ids.show_difficulty_img, 10.0)
552 .w_h(20.0, 20.0)
553 .set(state.ids.show_towns_img, ui);
554 if Button::image(if show_towns {
555 self.imgs.checkbox_checked
556 } else {
557 self.imgs.checkbox
558 })
559 .w_h(18.0, 18.0)
560 .hover_image(if show_towns {
561 self.imgs.checkbox_checked_mo
562 } else {
563 self.imgs.checkbox_mo
564 })
565 .press_image(if show_towns {
566 self.imgs.checkbox_checked
567 } else {
568 self.imgs.checkbox_press
569 })
570 .right_from(state.ids.show_towns_img, 10.0)
571 .set(state.ids.show_towns_box, ui)
572 .was_clicked()
573 {
574 events.push(Event::SettingsChange(MapShowTowns(!show_towns)));
575 }
576 Text::new(&i18n.get_msg("hud-map-towns"))
577 .right_from(state.ids.show_towns_box, 10.0)
578 .font_size(self.fonts.cyri.scale(14))
579 .font_id(self.fonts.cyri.conrod_id)
580 .graphics_for(state.ids.show_towns_box)
581 .color(TEXT_COLOR)
582 .set(state.ids.show_towns_text, ui);
583 Image::new(self.imgs.mmap_site_castle)
585 .down_from(state.ids.show_towns_img, 10.0)
586 .w_h(20.0, 20.0)
587 .set(state.ids.show_castles_img, ui);
588 if Button::image(if show_castles {
589 self.imgs.checkbox_checked
590 } else {
591 self.imgs.checkbox
592 })
593 .w_h(18.0, 18.0)
594 .hover_image(if show_castles {
595 self.imgs.checkbox_checked_mo
596 } else {
597 self.imgs.checkbox_mo
598 })
599 .press_image(if show_castles {
600 self.imgs.checkbox_checked
601 } else {
602 self.imgs.checkbox_press
603 })
604 .right_from(state.ids.show_castles_img, 10.0)
605 .set(state.ids.show_castles_box, ui)
606 .was_clicked()
607 {
608 events.push(Event::SettingsChange(MapShowCastles(!show_castles)));
609 }
610 Text::new(&i18n.get_msg("hud-map-castles"))
611 .right_from(state.ids.show_castles_box, 10.0)
612 .font_size(self.fonts.cyri.scale(14))
613 .font_id(self.fonts.cyri.conrod_id)
614 .graphics_for(state.ids.show_castles_box)
615 .color(TEXT_COLOR)
616 .set(state.ids.show_castles_text, ui);
617 Image::new(self.imgs.mmap_site_bridge)
619 .down_from(state.ids.show_castles_img, 10.0)
620 .w_h(20.0, 20.0)
621 .set(state.ids.show_bridges_img, ui);
622 if Button::image(if show_bridges {
623 self.imgs.checkbox_checked
624 } else {
625 self.imgs.checkbox
626 })
627 .w_h(18.0, 18.0)
628 .hover_image(if show_bridges {
629 self.imgs.checkbox_checked_mo
630 } else {
631 self.imgs.checkbox_mo
632 })
633 .press_image(if show_bridges {
634 self.imgs.checkbox_checked
635 } else {
636 self.imgs.checkbox_press
637 })
638 .right_from(state.ids.show_bridges_img, 10.0)
639 .set(state.ids.show_bridges_box, ui)
640 .was_clicked()
641 {
642 events.push(Event::SettingsChange(MapShowBridges(!show_bridges)));
643 }
644 Text::new(&i18n.get_msg("hud-map-bridges"))
645 .right_from(state.ids.show_bridges_box, 10.0)
646 .font_size(self.fonts.cyri.scale(14))
647 .font_id(self.fonts.cyri.conrod_id)
648 .graphics_for(state.ids.show_bridges_box)
649 .color(TEXT_COLOR)
650 .set(state.ids.show_bridges_text, ui);
651 Image::new(self.imgs.mmap_site_dungeon)
653 .down_from(state.ids.show_bridges_img, 10.0)
654 .w_h(20.0, 20.0)
655 .set(state.ids.show_dungeons_img, ui);
656 if Button::image(if show_dungeons {
657 self.imgs.checkbox_checked
658 } else {
659 self.imgs.checkbox
660 })
661 .w_h(18.0, 18.0)
662 .hover_image(if show_dungeons {
663 self.imgs.checkbox_checked_mo
664 } else {
665 self.imgs.checkbox_mo
666 })
667 .press_image(if show_dungeons {
668 self.imgs.checkbox_checked
669 } else {
670 self.imgs.checkbox_press
671 })
672 .right_from(state.ids.show_dungeons_img, 10.0)
673 .set(state.ids.show_dungeons_box, ui)
674 .was_clicked()
675 {
676 events.push(Event::SettingsChange(MapShowDungeons(!show_dungeons)));
677 }
678 Text::new(&i18n.get_msg("hud-map-dungeons"))
679 .right_from(state.ids.show_dungeons_box, 10.0)
680 .font_size(self.fonts.cyri.scale(14))
681 .font_id(self.fonts.cyri.conrod_id)
682 .graphics_for(state.ids.show_dungeons_box)
683 .color(TEXT_COLOR)
684 .set(state.ids.show_dungeons_text, ui);
685 Image::new(self.imgs.mmap_site_cave)
687 .down_from(state.ids.show_dungeons_img, 10.0)
688 .w_h(20.0, 20.0)
689 .set(state.ids.show_caves_img, ui);
690 if Button::image(if show_caves {
691 self.imgs.checkbox_checked
692 } else {
693 self.imgs.checkbox
694 })
695 .w_h(18.0, 18.0)
696 .hover_image(if show_caves {
697 self.imgs.checkbox_checked_mo
698 } else {
699 self.imgs.checkbox_mo
700 })
701 .press_image(if show_caves {
702 self.imgs.checkbox_checked
703 } else {
704 self.imgs.checkbox_press
705 })
706 .right_from(state.ids.show_caves_img, 10.0)
707 .set(state.ids.show_caves_box, ui)
708 .was_clicked()
709 {
710 events.push(Event::SettingsChange(MapShowCaves(!show_caves)));
711 }
712 Text::new(&i18n.get_msg("hud-map-caves"))
713 .right_from(state.ids.show_caves_box, 10.0)
714 .font_size(self.fonts.cyri.scale(14))
715 .font_id(self.fonts.cyri.conrod_id)
716 .graphics_for(state.ids.show_caves_box)
717 .color(TEXT_COLOR)
718 .set(state.ids.show_caves_text, ui);
719 Image::new(self.imgs.mmap_site_tree)
721 .down_from(state.ids.show_caves_img, 10.0)
722 .w_h(20.0, 20.0)
723 .set(state.ids.show_trees_img, ui);
724 if Button::image(if show_trees {
725 self.imgs.checkbox_checked
726 } else {
727 self.imgs.checkbox
728 })
729 .w_h(18.0, 18.0)
730 .hover_image(if show_trees {
731 self.imgs.checkbox_checked_mo
732 } else {
733 self.imgs.checkbox_mo
734 })
735 .press_image(if show_trees {
736 self.imgs.checkbox_checked
737 } else {
738 self.imgs.checkbox_press
739 })
740 .right_from(state.ids.show_trees_img, 10.0)
741 .set(state.ids.show_trees_box, ui)
742 .was_clicked()
743 {
744 events.push(Event::SettingsChange(MapShowTrees(!show_trees)));
745 }
746 Text::new(&i18n.get_msg("hud-map-trees"))
747 .right_from(state.ids.show_trees_box, 10.0)
748 .font_size(self.fonts.cyri.scale(14))
749 .font_id(self.fonts.cyri.conrod_id)
750 .graphics_for(state.ids.show_trees_box)
751 .color(TEXT_COLOR)
752 .set(state.ids.show_trees_text, ui);
753 Image::new(self.imgs.mmap_poi_biome)
755 .down_from(state.ids.show_trees_img, 10.0)
756 .w_h(20.0, 20.0)
757 .set(state.ids.show_biomes_img, ui);
758 if Button::image(if show_biomes {
759 self.imgs.checkbox_checked
760 } else {
761 self.imgs.checkbox
762 })
763 .w_h(18.0, 18.0)
764 .hover_image(if show_biomes {
765 self.imgs.checkbox_checked_mo
766 } else {
767 self.imgs.checkbox_mo
768 })
769 .press_image(if show_biomes {
770 self.imgs.checkbox_checked
771 } else {
772 self.imgs.checkbox_press
773 })
774 .right_from(state.ids.show_biomes_img, 10.0)
775 .set(state.ids.show_biomes_box, ui)
776 .was_clicked()
777 {
778 events.push(Event::SettingsChange(MapShowBiomes(!show_biomes)));
779 }
780 Text::new(&i18n.get_msg("hud-map-biomes"))
781 .right_from(state.ids.show_biomes_box, 10.0)
782 .font_size(self.fonts.cyri.scale(14))
783 .font_id(self.fonts.cyri.conrod_id)
784 .graphics_for(state.ids.show_biomes_box)
785 .color(TEXT_COLOR)
786 .set(state.ids.show_biomes_text, ui);
787 Image::new(self.imgs.mmap_poi_peak)
789 .down_from(state.ids.show_biomes_img, 10.0)
790 .w_h(20.0, 20.0)
791 .set(state.ids.show_peaks_img, ui);
792 if Button::image(if show_peaks {
793 self.imgs.checkbox_checked
794 } else {
795 self.imgs.checkbox
796 })
797 .w_h(18.0, 18.0)
798 .hover_image(if show_peaks {
799 self.imgs.checkbox_checked_mo
800 } else {
801 self.imgs.checkbox_mo
802 })
803 .press_image(if show_peaks {
804 self.imgs.checkbox_checked
805 } else {
806 self.imgs.checkbox_press
807 })
808 .right_from(state.ids.show_peaks_img, 10.0)
809 .set(state.ids.show_peaks_box, ui)
810 .was_clicked()
811 {
812 events.push(Event::SettingsChange(MapShowPeaks(!show_peaks)));
813 }
814 Text::new(&i18n.get_msg("hud-map-peaks"))
815 .right_from(state.ids.show_peaks_box, 10.0)
816 .font_size(self.fonts.cyri.scale(14))
817 .font_id(self.fonts.cyri.conrod_id)
818 .graphics_for(state.ids.show_peaks_box)
819 .color(TEXT_COLOR)
820 .set(state.ids.show_peaks_text, ui);
821 Text::new(&i18n.get_msg("hud-map-peaks"))
822 .right_from(state.ids.show_peaks_box, 10.0)
823 .font_size(self.fonts.cyri.scale(14))
824 .font_id(self.fonts.cyri.conrod_id)
825 .graphics_for(state.ids.show_peaks_box)
826 .color(TEXT_COLOR)
827 .set(state.ids.show_peaks_text, ui);
828 Image::new(self.imgs.mmap_site_glider_course)
830 .down_from(state.ids.show_peaks_img, 10.0)
831 .w_h(20.0, 20.0)
832 .set(state.ids.show_glider_courses_img, ui);
833 if Button::image(if show_glider_courses {
834 self.imgs.checkbox_checked
835 } else {
836 self.imgs.checkbox
837 })
838 .w_h(18.0, 18.0)
839 .hover_image(if show_glider_courses {
840 self.imgs.checkbox_checked_mo
841 } else {
842 self.imgs.checkbox_mo
843 })
844 .press_image(if show_glider_courses {
845 self.imgs.checkbox_checked
846 } else {
847 self.imgs.checkbox_press
848 })
849 .right_from(state.ids.show_glider_courses_img, 10.0)
850 .set(state.ids.show_glider_courses_box, ui)
851 .was_clicked()
852 {
853 events.push(Event::SettingsChange(MapShowGliderCourses(
854 !show_glider_courses,
855 )));
856 }
857 Text::new(&i18n.get_msg("hud-map-glider_courses"))
858 .right_from(state.ids.show_glider_courses_box, 10.0)
859 .font_size(self.fonts.cyri.scale(14))
860 .font_id(self.fonts.cyri.conrod_id)
861 .graphics_for(state.ids.show_glider_courses_box)
862 .color(TEXT_COLOR)
863 .set(state.ids.show_glider_courses_text, ui);
864 Image::new(self.imgs.mmap_unknown)
866 .down_from(state.ids.show_glider_courses_img, 10.0)
867 .w_h(20.0, 20.0)
868 .set(state.ids.show_quests_img, ui);
869 if Button::image(if show_quests {
870 self.imgs.checkbox_checked
871 } else {
872 self.imgs.checkbox
873 })
874 .w_h(18.0, 18.0)
875 .hover_image(if show_quests {
876 self.imgs.checkbox_checked_mo
877 } else {
878 self.imgs.checkbox_mo
879 })
880 .press_image(if show_quests {
881 self.imgs.checkbox_checked
882 } else {
883 self.imgs.checkbox_press
884 })
885 .right_from(state.ids.show_quests_img, 10.0)
886 .set(state.ids.show_quests_box, ui)
887 .was_clicked()
888 {
889 events.push(Event::SettingsChange(MapShowQuests(!show_quests)));
890 }
891 Text::new(&i18n.get_msg("hud-map-quests"))
892 .right_from(state.ids.show_quests_box, 10.0)
893 .font_size(self.fonts.cyri.scale(14))
894 .font_id(self.fonts.cyri.conrod_id)
895 .graphics_for(state.ids.show_quests_box)
896 .color(TEXT_COLOR)
897 .set(state.ids.show_quests_text, ui);
898
899 const EXPOSE_VOXEL_MAP_TOGGLE_IN_UI: bool = false;
900 if EXPOSE_VOXEL_MAP_TOGGLE_IN_UI {
901 Image::new(self.imgs.mmap_poi_peak)
902 .down_from(state.ids.show_peaks_img, 10.0)
903 .w_h(20.0, 20.0)
904 .set(state.ids.show_voxel_map_img, ui);
905 if Button::image(if show_voxel_map {
906 self.imgs.checkbox_checked
907 } else {
908 self.imgs.checkbox
909 })
910 .w_h(18.0, 18.0)
911 .hover_image(if show_voxel_map {
912 self.imgs.checkbox_checked_mo
913 } else {
914 self.imgs.checkbox_mo
915 })
916 .press_image(if show_voxel_map {
917 self.imgs.checkbox_checked
918 } else {
919 self.imgs.checkbox_press
920 })
921 .right_from(state.ids.show_voxel_map_img, 10.0)
922 .set(state.ids.show_voxel_map_box, ui)
923 .was_clicked()
924 {
925 events.push(Event::SettingsChange(MapShowVoxelMap(!show_voxel_map)));
926 }
927 Text::new(&i18n.get_msg("hud-map-voxel_map"))
928 .right_from(state.ids.show_voxel_map_box, 10.0)
929 .font_size(self.fonts.cyri.scale(14))
930 .font_id(self.fonts.cyri.conrod_id)
931 .graphics_for(state.ids.show_voxel_map_box)
932 .color(TEXT_COLOR)
933 .set(state.ids.show_voxel_map_text, ui);
934 }
935 if state.ids.mmap_poi_icons.len() < self.client.pois().len() {
937 state.update(|state| {
938 state
939 .ids
940 .mmap_poi_icons
941 .resize(self.client.pois().len(), &mut ui.widget_id_generator())
942 });
943 state.update(|state| {
944 state
945 .ids
946 .mmap_poi_titles
947 .resize(self.client.pois().len(), &mut ui.widget_id_generator())
948 });
949 state.update(|state| {
950 state
951 .ids
952 .mmap_poi_title_bgs
953 .resize(self.client.pois().len(), &mut ui.widget_id_generator())
954 });
955 }
956
957 let markers = self
958 .client
959 .markers()
960 .chain(self.extra_markers.iter().map(|em| &em.marker))
961 .collect::<Vec<_>>();
962
963 if state.ids.mmap_site_icons.len() < markers.len() {
964 state.update(|state| {
965 state
966 .ids
967 .mmap_site_icons
968 .resize(markers.len(), &mut ui.widget_id_generator())
969 });
970 }
971 if state.ids.site_difs.len() < markers.len() {
972 state.update(|state| {
973 state
974 .ids
975 .site_difs
976 .resize(markers.len(), &mut ui.widget_id_generator())
977 });
978 }
979
980 let wpos_to_rpos_fade =
981 |wpos: Vec2<f32>, bounding_rect_size: Vec2<f32>, fade_start: f32| {
982 let rwpos = wpos - player_pos;
984 let rcpos = rwpos.wpos_to_cpos()
986 + drag.map(|e| e as f32);
988 let rpos = rcpos.map(|e| e * zoom as f32);
991
992 let dist_to_closest_map_edge =
993 (rpos.map2(map_size, |e, sz| sz as f32 / 2.0 - e.abs()) - bounding_rect_size)
994 .reduce_partial_min();
995 match dist_to_closest_map_edge {
996 x if x <= 0.0 => None,
997 x if x < fade_start => Some((
998 rpos,
999 1.0 - 2.0_f32.powf(-10.0 * x / fade_start),
1001 )),
1002 _ => Some((rpos, 1.0)),
1003 }
1004 };
1005
1006 for (i, marker) in markers.iter().enumerate() {
1007 let rside = zoom as f32 * 8.0 * 1.2;
1008
1009 let (rpos, fade) =
1010 match wpos_to_rpos_fade(marker.wpos, Vec2::from(rside / 2.0), rside / 2.0) {
1011 Some(rpos) => rpos,
1012 None => continue,
1013 };
1014
1015 let title = marker
1016 .label
1017 .as_ref()
1018 .map(|name| i18n.get_content(name))
1019 .map(Cow::Owned)
1020 .unwrap_or_else(|| match &marker.kind {
1021 MarkerKind::Unknown => i18n.get_msg("hud-map-unknown"),
1022 MarkerKind::Town => i18n.get_msg("hud-map-town"),
1023 MarkerKind::Castle => i18n.get_msg("hud-map-castle"),
1024 MarkerKind::Cave => i18n.get_msg("hud-map-cave"),
1025 MarkerKind::Tree => i18n.get_msg("hud-map-tree"),
1026 MarkerKind::Gnarling => i18n.get_msg("hud-map-gnarling"),
1027 MarkerKind::ChapelSite => i18n.get_msg("hud-map-chapel_site"),
1028 MarkerKind::Terracotta => i18n.get_msg("hud-map-terracotta"),
1029 MarkerKind::Bridge => i18n.get_msg("hud-map-bridge"),
1030 MarkerKind::GliderCourse => i18n.get_msg("hud-map-glider_course"),
1031 MarkerKind::Adlet => i18n.get_msg("hud-map-adlet"),
1032 MarkerKind::Haniwa => i18n.get_msg("hud-map-haniwa"),
1033 MarkerKind::Cultist => i18n.get_msg("hud-map-cultist"),
1034 MarkerKind::Sahagin => i18n.get_msg("hud-map-sahagin"),
1035 MarkerKind::Myrmidon => i18n.get_msg("hud-map-myrmidon"),
1036 MarkerKind::DwarvenMine => i18n.get_msg("hud-map-df_mine"),
1037 MarkerKind::VampireCastle => i18n.get_msg("hud-map-vampire_castle"),
1038 MarkerKind::Character => i18n.get_msg("hud-map-character"),
1039 });
1040 let (difficulty, desc) = match &marker.kind {
1041 MarkerKind::Unknown => (None, i18n.get_msg("hud-map-unknown")),
1042 MarkerKind::Town => (None, i18n.get_msg("hud-map-town")),
1043 MarkerKind::Castle => (None, i18n.get_msg("hud-map-castle")),
1044 MarkerKind::Cave => (None, i18n.get_msg("hud-map-cave")),
1045 MarkerKind::Tree => (None, i18n.get_msg("hud-map-tree")),
1046 MarkerKind::Gnarling => (Some(0), i18n.get_msg("hud-map-gnarling")),
1047 MarkerKind::Terracotta => (Some(5), i18n.get_msg("hud-map-terracotta")),
1048 MarkerKind::ChapelSite => (Some(4), i18n.get_msg("hud-map-chapel_site")),
1049 MarkerKind::Bridge => (None, i18n.get_msg("hud-map-bridge")),
1050 MarkerKind::GliderCourse => (None, i18n.get_msg("hud-map-glider_course")),
1051 MarkerKind::Adlet => (Some(1), i18n.get_msg("hud-map-adlet")),
1052 MarkerKind::Haniwa => (Some(3), i18n.get_msg("hud-map-haniwa")),
1053 MarkerKind::Cultist => (Some(5), i18n.get_msg("hud-map-cultist")),
1054 MarkerKind::Sahagin => (Some(2), i18n.get_msg("hud-map-sahagin")),
1055 MarkerKind::Myrmidon => (Some(4), i18n.get_msg("hud-map-myrmidon")),
1056 MarkerKind::DwarvenMine => (Some(5), i18n.get_msg("hud-map-df_mine")),
1057 MarkerKind::VampireCastle => (Some(3), i18n.get_msg("hud-map-vampire_castle")),
1058 MarkerKind::Character => (None, i18n.get_msg("hud-map-character")),
1059 };
1060 let desc = if let Some(site_id) = marker.site
1061 && let Some(site) = self.client.sites().get(&site_id)
1062 {
1063 desc.into_owned() + &get_site_economy(site)
1064 } else {
1065 desc.into_owned()
1066 };
1067 let site_btn = Button::image(match &marker.kind {
1068 MarkerKind::Unknown => self.imgs.mmap_unknown,
1069 MarkerKind::Town => self.imgs.mmap_site_town,
1070 MarkerKind::ChapelSite => self.imgs.mmap_site_sea_chapel,
1071 MarkerKind::Terracotta => self.imgs.mmap_site_terracotta,
1072 MarkerKind::Castle => self.imgs.mmap_site_castle,
1073 MarkerKind::Cave => self.imgs.mmap_site_cave,
1074 MarkerKind::Tree => self.imgs.mmap_site_tree,
1075 MarkerKind::Gnarling => self.imgs.mmap_site_gnarling,
1076 MarkerKind::Adlet => self.imgs.mmap_site_adlet,
1077 MarkerKind::Haniwa => self.imgs.mmap_site_haniwa,
1078 MarkerKind::Cultist => self.imgs.mmap_site_cultist,
1079 MarkerKind::Sahagin => self.imgs.mmap_site_sahagin,
1080 MarkerKind::Myrmidon => self.imgs.mmap_site_myrmidon,
1081 MarkerKind::DwarvenMine => self.imgs.mmap_site_mine,
1082 MarkerKind::VampireCastle => self.imgs.mmap_site_vampire_castle,
1083
1084 MarkerKind::Bridge => self.imgs.mmap_site_bridge,
1085 MarkerKind::GliderCourse => self.imgs.mmap_site_glider_course,
1086 MarkerKind::Character => self.imgs.mmap_character,
1087 })
1088 .x_y_position_relative_to(
1089 state.ids.map_layers[0],
1090 position::Relative::Scalar(rpos.x as f64),
1091 position::Relative::Scalar(rpos.y as f64),
1092 )
1093 .w_h(rside as f64, rside as f64)
1094 .hover_image(match &marker.kind {
1095 MarkerKind::Unknown => self.imgs.mmap_unknown_hover,
1096 MarkerKind::Town => self.imgs.mmap_site_town_hover,
1097 MarkerKind::ChapelSite => self.imgs.mmap_site_sea_chapel_hover,
1098 MarkerKind::Terracotta => self.imgs.mmap_site_terracotta_hover,
1099 MarkerKind::Castle => self.imgs.mmap_site_castle_hover,
1100 MarkerKind::Cave => self.imgs.mmap_site_cave_hover,
1101 MarkerKind::Tree => self.imgs.mmap_site_tree_hover,
1102 MarkerKind::Gnarling => self.imgs.mmap_site_gnarling_hover,
1103 MarkerKind::Adlet => self.imgs.mmap_site_adlet_hover,
1104 MarkerKind::Haniwa => self.imgs.mmap_site_haniwa_hover,
1105 MarkerKind::Cultist => self.imgs.mmap_site_cultist_hover,
1106 MarkerKind::Sahagin => self.imgs.mmap_site_sahagin_hover,
1107 MarkerKind::Myrmidon => self.imgs.mmap_site_myrmidon_hover,
1108 MarkerKind::DwarvenMine => self.imgs.mmap_site_mine_hover,
1109 MarkerKind::VampireCastle => self.imgs.mmap_site_vampire_castle_hover,
1110 MarkerKind::Bridge => self.imgs.mmap_site_bridge_hover,
1111 MarkerKind::GliderCourse => self.imgs.mmap_site_glider_course_hover,
1112 MarkerKind::Character => self.imgs.mmap_character_hover,
1113 });
1114 let site_btn = if let Some(color) = marker_color(marker, self.pulse) {
1115 site_btn.image_color(color.alpha(fade))
1116 } else {
1117 site_btn.image_color(UI_HIGHLIGHT_0.alpha(fade))
1118 };
1119 let site_btn = site_btn.with_tooltip(
1120 self.tooltip_manager,
1121 &title,
1122 &desc,
1123 &site_tooltip,
1124 match &marker.kind {
1125 MarkerKind::Gnarling
1126 | MarkerKind::ChapelSite
1127 | MarkerKind::Terracotta
1128 | MarkerKind::Adlet
1129 | MarkerKind::VampireCastle
1130 | MarkerKind::Haniwa
1131 | MarkerKind::Cultist
1132 | MarkerKind::Sahagin
1133 | MarkerKind::Myrmidon
1134 | MarkerKind::DwarvenMine => match difficulty {
1135 Some(0) => QUALITY_LOW,
1136 Some(1) => QUALITY_COMMON,
1137 Some(2) => QUALITY_MODERATE,
1138 Some(3) => QUALITY_HIGH,
1139 Some(4 | 5) => QUALITY_EPIC,
1140 _ => TEXT_COLOR,
1141 },
1142 _ => TEXT_COLOR,
1143 },
1144 );
1145
1146 handle_widget_mouse_events(
1147 state.ids.mmap_site_icons[i],
1148 MarkerChange::Pos(marker.wpos),
1149 ui,
1150 &mut events,
1151 state.ids.map_layers[0],
1152 );
1153
1154 let show_site = match &marker.kind {
1156 _ if marker.flags.contains(MarkerFlags::IS_QUEST) => show_quests,
1157 MarkerKind::Unknown => true,
1158 MarkerKind::Town => show_towns,
1159 MarkerKind::Gnarling
1160 | MarkerKind::ChapelSite
1161 | MarkerKind::DwarvenMine
1162 | MarkerKind::Haniwa
1163 | MarkerKind::Cultist
1164 | MarkerKind::Sahagin
1165 | MarkerKind::Myrmidon
1166 | MarkerKind::Terracotta
1167 | MarkerKind::Adlet
1168 | MarkerKind::VampireCastle => show_dungeons,
1169 MarkerKind::Castle => show_castles,
1170 MarkerKind::Cave => show_caves,
1171 MarkerKind::Tree => show_trees,
1172 MarkerKind::Bridge => show_bridges,
1173 MarkerKind::GliderCourse => show_glider_courses,
1174 MarkerKind::Character => true, };
1176 if show_site {
1177 let tooltip_visible = site_btn.set_ext(state.ids.mmap_site_icons[i], ui).1;
1178
1179 if SHOW_ECONOMY
1180 && tooltip_visible
1181 && let Some(site_id) = marker.site
1182 && let Some(site) = self.client.sites().get(&site_id)
1183 && site.economy.is_none()
1184 {
1185 events.push(Event::RequestSiteInfo(site_id));
1186 }
1187 }
1188
1189 if show_difficulty {
1192 let rsize = zoom * 2.4; let dif_img = Image::new(match difficulty {
1194 Some(0) => self.imgs.map_dif_1,
1195 Some(1) => self.imgs.map_dif_2,
1196 Some(2) => self.imgs.map_dif_3,
1197 Some(3) => self.imgs.map_dif_4,
1198 Some(4 | 5) => self.imgs.map_dif_5,
1199 Some(_) => self.imgs.map_dif_unknown,
1200 None => self.imgs.nothing,
1201 })
1202 .mid_top_with_margin_on(state.ids.mmap_site_icons[i], match difficulty {
1203 Some(0 | 1) => -1.0 * rsize,
1204 Some(_) => -2.0 * rsize,
1205 _ => -1.0 * rsize,
1206 })
1207 .w(match difficulty {
1208 Some(0) => 1.0 * rsize,
1209 Some(1 | 2) => 2.0 * rsize,
1210 Some(_) => 3.0 * rsize,
1211 _ => 1.0 * rsize,
1212 })
1213 .h(match difficulty {
1214 Some(0 | 1) => 1.0 * rsize,
1215 Some(_) => 2.0 * rsize,
1216 _ => 1.0 * rsize,
1217 })
1218 .color(Some(match difficulty {
1219 Some(0) => QUALITY_LOW,
1220 Some(1) => QUALITY_COMMON,
1221 Some(2) => QUALITY_MODERATE,
1222 Some(3) => QUALITY_HIGH,
1223 Some(4 | 5) => QUALITY_EPIC, _ => TEXT_COLOR,
1225 }));
1226 match &marker.kind {
1227 MarkerKind::Unknown | MarkerKind::Character => {
1228 dif_img.set(state.ids.site_difs[i], ui)
1229 },
1230 MarkerKind::Town => {
1231 if show_towns {
1232 dif_img.set(state.ids.site_difs[i], ui)
1233 }
1234 },
1235 MarkerKind::Gnarling
1236 | MarkerKind::ChapelSite
1237 | MarkerKind::Haniwa
1238 | MarkerKind::Cultist
1239 | MarkerKind::Sahagin
1240 | MarkerKind::Myrmidon
1241 | MarkerKind::Terracotta
1242 | MarkerKind::Adlet
1243 | MarkerKind::VampireCastle => {
1244 if show_dungeons {
1245 dif_img.set(state.ids.site_difs[i], ui)
1246 }
1247 },
1248 MarkerKind::DwarvenMine => {
1249 if show_dungeons {
1250 dif_img.set(state.ids.site_difs[i], ui)
1251 }
1252 },
1253 MarkerKind::Castle => {
1254 if show_castles {
1255 dif_img.set(state.ids.site_difs[i], ui)
1256 }
1257 },
1258 MarkerKind::Cave => {
1259 if show_caves {
1260 dif_img.set(state.ids.site_difs[i], ui)
1261 }
1262 },
1263 MarkerKind::Tree => {
1264 if show_trees {
1265 dif_img.set(state.ids.site_difs[i], ui)
1266 }
1267 },
1268 MarkerKind::Bridge => {
1269 if show_bridges {
1270 dif_img.set(state.ids.site_difs[i], ui)
1271 }
1272 },
1273 MarkerKind::GliderCourse => {
1274 if show_glider_courses {
1275 dif_img.set(state.ids.site_difs[i], ui)
1276 }
1277 },
1278 }
1279
1280 handle_widget_mouse_events(
1281 state.ids.site_difs[i],
1282 MarkerChange::Pos(marker.wpos),
1283 ui,
1284 &mut events,
1285 state.ids.map_layers[0],
1286 );
1287 }
1288 }
1289 for (i, poi) in self.client.pois().iter().enumerate() {
1290 let (rpos, fade) = match wpos_to_rpos_fade(
1293 poi.wpos.map(|e| e as f32),
1294 Vec2::from(zoom as f32 * 3.0),
1295 zoom as f32 * 5.0,
1296 ) {
1297 Some(rpos) => rpos,
1298 None => continue,
1299 };
1300 let title = &poi.name;
1301 match poi.kind {
1302 PoiKind::Peak(alt) => {
1303 let height = format!("{} m", alt);
1304 if show_peaks && zoom > 2.0 {
1305 Text::new(title)
1306 .x_y_position_relative_to(
1307 state.ids.map_layers[0],
1308 position::Relative::Scalar(rpos.x as f64),
1309 position::Relative::Scalar(rpos.y as f64 + zoom * 4.0),
1310 )
1311 .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32))
1312 .font_id(self.fonts.cyri.conrod_id)
1313 .graphics_for(state.ids.map_layers[0])
1314 .color(TEXT_BG.alpha(fade))
1315 .set(state.ids.mmap_poi_title_bgs[i], ui);
1316 Text::new(title)
1317 .bottom_left_with_margins_on(state.ids.mmap_poi_title_bgs[i], 1.0, 1.0)
1318 .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32))
1319 .font_id(self.fonts.cyri.conrod_id)
1320 .color(TEXT_COLOR.alpha(fade))
1322 .set(state.ids.mmap_poi_titles[i], ui);
1323
1324 handle_widget_mouse_events(
1325 state.ids.mmap_poi_titles[i],
1326 MarkerChange::Pos(poi.wpos.map(|e| e as f32)),
1327 ui,
1328 &mut events,
1329 state.ids.map_layers[0],
1330 );
1331
1332 if ui
1334 .widget_input(state.ids.mmap_poi_titles[i])
1335 .mouse()
1336 .is_some_and(|m| m.is_over())
1337 {
1338 Text::new(&height)
1339 .mid_bottom_with_margin_on(
1340 state.ids.mmap_poi_title_bgs[i],
1341 zoom * 3.5,
1342 )
1343 .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32))
1344 .font_id(self.fonts.cyri.conrod_id)
1345 .graphics_for(state.ids.map_layers[0])
1346 .color(TEXT_BG.alpha(fade))
1347 .set(state.ids.peaks_txt_bg, ui);
1348 Text::new(&height)
1349 .bottom_left_with_margins_on(state.ids.peaks_txt_bg, 1.0, 1.0)
1350 .font_size(self.fonts.cyri.scale((zoom * 3.0) as u32))
1351 .font_id(self.fonts.cyri.conrod_id)
1352 .graphics_for(state.ids.map_layers[0])
1353 .color(TEXT_COLOR.alpha(fade))
1354 .set(state.ids.peaks_txt, ui);
1355 }
1356 }
1357 },
1358 PoiKind::Lake(size) => {
1359 if show_biomes && zoom > 2.0 && zoom.powi(2) * size as f64 > 30.0 {
1360 let font_scale_factor = if size > 20 {
1361 size as f64 / 25.0
1362 } else if size > 10 {
1363 size as f64 / 10.0
1364 } else if size > 5 {
1365 size as f64 / 6.0
1366 } else {
1367 size as f64 / 2.5
1368 };
1369 Text::new(title)
1370 .x_y_position_relative_to(
1371 state.ids.map_layers[0],
1372 position::Relative::Scalar(rpos.x as f64),
1373 position::Relative::Scalar(rpos.y as f64),
1374 )
1375 .font_size(
1376 self.fonts.cyri.scale(
1377 (2.0 + font_scale_factor * zoom).clamp(10.0, 18.0) as u32,
1378 ),
1379 )
1380 .font_id(self.fonts.cyri.conrod_id)
1381 .graphics_for(state.ids.map_layers[0])
1382 .color(TEXT_BLUE_COLOR.alpha(fade))
1383 .set(state.ids.mmap_poi_icons[i], ui);
1384 }
1385 },
1386 }
1387 }
1388 let client_state = self.client.state();
1390 let stats = client_state.ecs().read_storage::<comp::Stats>();
1391 let member_pos = client_state.ecs().read_storage::<comp::Pos>();
1392 let group_members = self
1393 .client
1394 .group_members()
1395 .iter()
1396 .filter_map(|(u, r)| match r {
1397 Role::Member => Some(u),
1398 Role::Pet => None,
1399 })
1400 .collect::<Vec<_>>();
1401 let group_size = group_members.len();
1402 let id_maps = client_state
1404 .ecs()
1405 .read_resource::<common_net::sync::IdMaps>();
1406 if state.ids.member_indicators.len() < group_size {
1407 state.update(|s| {
1408 s.ids
1409 .member_indicators
1410 .resize(group_size, &mut ui.widget_id_generator())
1411 })
1412 };
1413 for (i, &uid) in group_members.iter().copied().enumerate() {
1414 let entity = id_maps.uid_entity(uid);
1415 let member_pos = entity.and_then(|entity| member_pos.get(entity));
1416 let stats = entity.and_then(|entity| stats.get(entity));
1417 let name = if let Some(stats) = stats {
1418 i18n.get_content(&stats.name)
1419 } else {
1420 "".to_string()
1421 };
1422
1423 if let Some(member_pos) = member_pos {
1424 let factor = 1.2;
1425 let side_length = 20.0 * factor;
1426
1427 let (rpos, fade) = match wpos_to_rpos_fade(
1428 member_pos.0.xy(),
1429 Vec2::from(side_length / 2.0),
1430 side_length / 2.0,
1431 ) {
1432 Some(x) => x,
1433 None => continue,
1434 };
1435
1436 let z_comparison = (member_pos.0.z - player_pos.z) as i32;
1437
1438 Button::image(match z_comparison {
1439 10..=i32::MAX => self.imgs.indicator_group_up,
1440 i32::MIN..=-10 => self.imgs.indicator_group_down,
1441 _ => self.imgs.indicator_group,
1442 })
1443 .x_y_position_relative_to(
1444 state.ids.map_layers[0],
1445 position::Relative::Scalar(rpos.x as f64),
1446 position::Relative::Scalar(rpos.y as f64),
1447 )
1448 .w_h(side_length as f64, side_length as f64)
1449 .image_color(Color::Rgba(1.0, 1.0, 1.0, fade))
1450 .floating(true)
1451 .with_tooltip(self.tooltip_manager, &name, "", &site_tooltip, TEXT_COLOR)
1452 .set(state.ids.member_indicators[i], ui);
1453
1454 handle_widget_mouse_events(
1455 state.ids.member_indicators[i],
1456 MarkerChange::Pos(member_pos.0.xy()),
1457 ui,
1458 &mut events,
1459 state.ids.map_layers[0],
1460 );
1461 }
1462 }
1463
1464 let factor = 1.4;
1465 let side_length = 20.0 * factor;
1466 if state.ids.location_marker_group.len() < self.location_markers.group.len() {
1468 state.update(|s| {
1469 s.ids.location_marker_group.resize(
1470 self.location_markers.group.len(),
1471 &mut ui.widget_id_generator(),
1472 )
1473 })
1474 };
1475 for (i, (&uid, &rpos)) in self.location_markers.group.iter().enumerate() {
1476 let lm = rpos.as_();
1477 if let Some((rpos, fade)) =
1478 wpos_to_rpos_fade(lm, Vec2::from(side_length / 2.0), side_length / 2.0)
1479 {
1480 let name = self
1481 .client
1482 .player_list()
1483 .get(&uid)
1484 .map(|info| info.player_alias.clone())
1485 .or_else(|| {
1486 id_maps
1487 .uid_entity(uid)
1488 .and_then(|entity| stats.get(entity))
1489 .map(|stats| i18n.get_content(&stats.name))
1490 })
1491 .unwrap_or(String::new());
1492
1493 let image_id = match self.client.group_info().map(|info| info.1) {
1494 Some(leader) if leader == uid => self.imgs.location_marker_group_leader,
1495 _ => self.imgs.location_marker_group,
1496 };
1497
1498 Button::image(image_id)
1499 .x_y_position_relative_to(
1500 state.ids.map_layers[0],
1501 position::Relative::Scalar(rpos.x as f64),
1502 position::Relative::Scalar(rpos.y as f64 + 10.0 * factor as f64),
1503 )
1504 .w_h(side_length as f64, side_length as f64)
1505 .image_color(Color::Rgba(1.0, 1.0, 1.0, fade))
1506 .floating(true)
1507 .with_tooltip(
1508 self.tooltip_manager,
1509 &i18n.get_msg("hud-map-marked_location"),
1510 &format!(
1511 "X: {}, Y: {}\n\n{}",
1512 lm.x as i32,
1513 lm.y as i32,
1514 i18n.get_msg_ctx("hud-map-placed_by", &i18n::fluent_args! {
1515 "name" => name
1516 }),
1517 ),
1518 &site_tooltip,
1519 TEXT_VELORITE,
1520 )
1521 .set(state.ids.location_marker_group[i], ui);
1522 handle_widget_mouse_events(
1523 state.ids.location_marker_group[i],
1524 MarkerChange::Pos(lm),
1525 ui,
1526 &mut events,
1527 state.ids.map_layers[0],
1528 );
1529 }
1530 }
1531 if let Some((lm, (rpos, fade))) = self.location_markers.owned.and_then(|lm| {
1533 let lm = lm.as_();
1534 Some(lm).zip(wpos_to_rpos_fade(
1535 lm,
1536 Vec2::from(side_length / 2.0),
1537 side_length / 2.0,
1538 ))
1539 }) {
1540 if Button::image(self.imgs.location_marker)
1541 .x_y_position_relative_to(
1542 state.ids.map_layers[0],
1543 position::Relative::Scalar(rpos.x as f64),
1544 position::Relative::Scalar(rpos.y as f64 + 10.0 * factor as f64),
1545 )
1546 .w_h(side_length as f64, side_length as f64)
1547 .image_color(Color::Rgba(1.0, 1.0, 1.0, fade))
1548 .floating(true)
1549 .with_tooltip(
1550 self.tooltip_manager,
1551 &i18n.get_msg("hud-map-marked_location"),
1552 &format!(
1553 "X: {}, Y: {}\n\n{}",
1554 lm.x as i32,
1555 lm.y as i32,
1556 i18n.get_msg("hud-map-marked_location_remove")
1557 ),
1558 &site_tooltip,
1559 TEXT_VELORITE,
1560 )
1561 .set(state.ids.location_marker, ui)
1562 .was_clicked()
1563 {
1564 events.push(Event::RemoveMarker);
1565 }
1566
1567 handle_widget_mouse_events(
1568 state.ids.location_marker,
1569 MarkerChange::Remove,
1570 ui,
1571 &mut events,
1572 state.ids.map_layers[0],
1573 );
1574 }
1575
1576 let arrow_sz = {
1582 let scale = 0.5;
1583 Vec2::new(36.0, 37.0) * scale
1584 };
1585 if let Some((rpos, fade)) =
1587 wpos_to_rpos_fade(player_pos.xy(), arrow_sz, arrow_sz.reduce_partial_min())
1588 {
1589 let ind = {
1590 if colored_player_marker {
1591 self.rot_imgs.indicator_mmap_colored.target_north
1592 } else {
1593 self.rot_imgs.indicator_mmap.target_north
1594 }
1595 };
1596 Image::new(ind)
1597 .x_y_position_relative_to(
1598 state.ids.map_layers[0],
1599 position::Relative::Scalar(rpos.x as f64),
1600 position::Relative::Scalar(rpos.y as f64),
1601 )
1602 .w_h(arrow_sz.x as f64, arrow_sz.y as f64)
1603 .color(Some(UI_HIGHLIGHT_0.alpha(fade)))
1604 .set(state.ids.indicator, ui);
1605
1606 handle_widget_mouse_events(
1607 state.ids.indicator,
1608 MarkerChange::Pos(player_pos.xy()),
1609 ui,
1610 &mut events,
1611 state.ids.map_layers[0],
1612 );
1613 }
1614
1615 let icon_size = Vec2::new(25.6, 28.8);
1617 let recenter: bool = drag.x != 0.0 || drag.y != 0.0;
1618 if Button::image(self.imgs.button)
1619 .w_h(92.0, icon_size.y)
1620 .mid_bottom_with_margin_on(state.ids.map_layers[0], -36.0)
1621 .hover_image(if recenter {
1622 self.imgs.button_hover
1623 } else {
1624 self.imgs.button
1625 })
1626 .press_image(if recenter {
1627 self.imgs.button_press
1628 } else {
1629 self.imgs.button
1630 })
1631 .label(&i18n.get_msg("hud-map-recenter"))
1632 .label_y(position::Relative::Scalar(1.0))
1633 .label_color(if recenter {
1634 TEXT_COLOR
1635 } else {
1636 TEXT_GRAY_COLOR
1637 })
1638 .image_color(if recenter {
1639 TEXT_COLOR
1640 } else {
1641 TEXT_GRAY_COLOR
1642 })
1643 .label_font_size(self.fonts.cyri.scale(12))
1644 .label_font_id(self.fonts.cyri.conrod_id)
1645 .set(state.ids.recenter_button, ui)
1646 .was_clicked()
1647 {
1648 events.push(Event::MapDrag(Vec2::zero()));
1649 };
1650
1651 Image::new(self.imgs.m_move_ico)
1652 .bottom_left_with_margins_on(state.ids.map_layers[0], -36.0, 0.0)
1653 .w_h(icon_size.x, icon_size.y)
1654 .color(Some(UI_HIGHLIGHT_0))
1655 .set(state.ids.drag_ico, ui);
1656 Text::new(&i18n.get_msg("hud-map-drag"))
1657 .right_from(state.ids.drag_ico, 5.0)
1658 .font_size(self.fonts.cyri.scale(14))
1659 .font_id(self.fonts.cyri.conrod_id)
1660 .graphics_for(state.ids.map_layers[0])
1661 .color(TEXT_COLOR)
1662 .set(state.ids.drag_txt, ui);
1663 Image::new(self.imgs.m_scroll_ico)
1664 .right_from(state.ids.drag_txt, 5.0)
1665 .w_h(icon_size.x, icon_size.y)
1666 .color(Some(UI_HIGHLIGHT_0))
1667 .set(state.ids.zoom_ico, ui);
1668 Text::new(&i18n.get_msg("hud-map-zoom"))
1669 .right_from(state.ids.zoom_ico, 5.0)
1670 .font_size(self.fonts.cyri.scale(14))
1671 .font_id(self.fonts.cyri.conrod_id)
1672 .graphics_for(state.ids.map_layers[0])
1673 .color(TEXT_COLOR)
1674 .set(state.ids.zoom_txt, ui);
1675
1676 Text::new(&location_marker_binding.display_shortest())
1677 .right_from(state.ids.zoom_txt, 15.0)
1678 .font_size(self.fonts.cyri.scale(14))
1679 .font_id(self.fonts.cyri.conrod_id)
1680 .graphics_for(state.ids.map_layers[0])
1681 .color(TEXT_COLOR)
1682 .set(state.ids.waypoint_binding_txt, ui);
1683
1684 Text::new(&i18n.get_msg("hud-map-mid_click"))
1685 .right_from(state.ids.waypoint_binding_txt, 5.0)
1686 .font_size(self.fonts.cyri.scale(14))
1687 .font_id(self.fonts.cyri.conrod_id)
1688 .graphics_for(state.ids.map_layers[0])
1689 .color(TEXT_COLOR)
1690 .set(state.ids.waypoint_txt, ui);
1691
1692 if Button::image(self.imgs.button)
1694 .w_h(92.0, icon_size.y)
1695 .hover_image(self.imgs.button_hover)
1696 .press_image(self.imgs.button_press)
1697 .bottom_right_with_margins_on(state.ids.map_layers[0], -36.0, 0.0)
1698 .with_tooltip(
1699 self.tooltip_manager,
1700 &i18n.get_msg("hud-map-change_map_mode"),
1701 "",
1702 &site_tooltip,
1703 TEXT_COLOR,
1704 )
1705 .set(state.ids.map_mode_btn, ui)
1706 .was_clicked()
1707 {
1708 events.push(Event::SettingsChange(MapShowTopoMap(!show_topo_map)));
1709 };
1710 Button::image(self.imgs.map_mode_overlay)
1711 .w_h(92.0, icon_size.y)
1712 .graphics_for(state.ids.map_mode_btn)
1713 .middle_of(state.ids.map_mode_btn)
1714 .set(state.ids.map_mode_overlay, ui);
1715
1716 if Button::image(self.imgs.button)
1718 .w_h(92.0, icon_size.y)
1719 .hover_image(self.imgs.button_hover)
1720 .press_image(self.imgs.button_press)
1721 .left_from(state.ids.map_mode_btn, 5.0)
1722 .with_tooltip(
1723 self.tooltip_manager,
1724 &i18n.get_msg("hud-map-toggle_minimap_voxel"),
1725 &i18n.get_msg("hud-map-zoom_minimap_explanation"),
1726 &site_tooltip,
1727 TEXT_COLOR,
1728 )
1729 .set(state.ids.minimap_mode_btn, ui)
1730 .was_clicked()
1731 {
1732 events.push(Event::SettingsChange(MapShowVoxelMap(!show_voxel_map)));
1733 };
1734 Button::image(self.imgs.minimap_mode_overlay)
1735 .w_h(92.0, icon_size.y)
1736 .graphics_for(state.ids.minimap_mode_btn)
1737 .middle_of(state.ids.minimap_mode_btn)
1738 .set(state.ids.minimap_mode_overlay, ui);
1739
1740 events
1741 }
1742}
1743
1744pub fn marker_color(marker: &Marker, pulse: f32) -> Option<Color> {
1745 if marker.flags.contains(MarkerFlags::IS_QUEST) {
1746 Some(Color::Rgba(
1747 0.3,
1748 1.0,
1749 0.85,
1750 0.75 + (pulse * 4.0).sin() * 0.25,
1751 ))
1752 } else {
1753 None
1754 }
1755}