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