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