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