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