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