1use super::{
2 CRITICAL_HP_COLOR, HudInfo, LOW_HP_COLOR, Show, SlotGrid, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
3 cr_color,
4 img_ids::{Imgs, ImgsRot},
5 item_imgs::ItemImgs,
6 slots::{ArmorSlot, EquipSlot, SlotManager},
7};
8use crate::{
9 GlobalState,
10 game_input::GameInput,
11 settings::{
12 HudPositionSettings,
13 hud_position::{
14 DEFAULT_OTHER_BAG_HEIGHT, DEFAULT_OTHER_BAG_WIDTH, DEFAULT_OWN_BAG_HEIGHT,
15 DEFAULT_OWN_BAG_WIDTH,
16 },
17 },
18 ui::{
19 ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip, TooltipManager,
20 Tooltipable,
21 fonts::Fonts,
22 slot::{ContentSize, SlotMaker},
23 },
24 window::MenuInput,
25};
26use client::Client;
27use common::{
28 combat::{Damage, combat_rating, perception_dist_multiplier_from_stealth},
29 comp::{
30 Body, Energy, Health, Inventory, Poise, SkillSet, Stats,
31 inventory::InventorySortOrder,
32 item::{ItemDesc, ItemI18n, MaterialStatManifest},
33 },
34 recipe::RecipeBookManifest,
35};
36use conrod_core::{
37 Color, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, color,
38 widget::{self, Button, Image, Rectangle, Scrollbar, State as ConrodState, Text},
39 widget_ids,
40};
41use i18n::Localization;
42use std::borrow::Cow;
43
44use specs::Entity as EcsEntity;
45use vek::{Vec2, approx::AbsDiffEq};
46
47widget_ids! {
48 pub struct InventoryScrollerIds {
49 draggable_area,
50 inv_alignment,
51 slot_grid,
52 space_txt,
54 inventory_title,
56 inventory_title_bg,
57 scrollbar_bg,
58 second_phase_scrollbar_bg,
59 scrollbar_slots,
60 left_scrollbar_slots,
61 }
62}
63
64pub struct InventoryScrollerState {
65 ids: InventoryScrollerIds,
66}
67
68pub enum InventoryScrollerEvent {
69 Drag(Vec2<f64>),
70 ChangeLocalFocus(usize),
71 Close,
72}
73
74#[derive(WidgetCommon)]
75pub struct InventoryScroller<'a> {
76 client: &'a Client,
77 imgs: &'a Imgs,
78 item_imgs: &'a ItemImgs,
79 fonts: &'a Fonts,
80 #[conrod(common_builder)]
81 common: widget::CommonBuilder,
82 item_tooltip_manager: &'a mut ItemTooltipManager,
83 slot_manager: &'a mut SlotManager,
84 pulse: f32,
85 menu_events: &'a Vec<MenuInput>,
86 active_content: usize,
87 localized_strings: &'a Localization,
88 item_i18n: &'a ItemI18n,
89 show_stats: bool,
90 show_bag_inv: bool,
91 on_right: bool,
92 global_state: &'a GlobalState,
93 item_tooltip: &'a ItemTooltip<'a>,
94 playername: String,
95 entity: EcsEntity,
96 is_us: bool,
97 inventory: &'a Inventory,
98 bg_ids: &'a BackgroundIds,
99 show_salvage: bool,
100 details_mode: bool,
101}
102
103impl<'a> InventoryScroller<'a> {
104 #[expect(clippy::too_many_arguments)]
105 pub fn new(
106 client: &'a Client,
107 global_state: &'a GlobalState,
108 imgs: &'a Imgs,
109 item_imgs: &'a ItemImgs,
110 fonts: &'a Fonts,
111 item_tooltip_manager: &'a mut ItemTooltipManager,
112 slot_manager: &'a mut SlotManager,
113 pulse: f32,
114 menu_events: &'a Vec<MenuInput>,
115 active_content: usize,
116 localized_strings: &'a Localization,
117 item_i18n: &'a ItemI18n,
118 show_stats: bool,
119 show_bag_inv: bool,
120 on_right: bool,
121 item_tooltip: &'a ItemTooltip<'a>,
122 playername: String,
123 entity: EcsEntity,
124 is_us: bool,
125 inventory: &'a Inventory,
126 bg_ids: &'a BackgroundIds,
127 show_salvage: bool,
128 details_mode: bool,
129 ) -> Self {
130 InventoryScroller {
131 client,
132 imgs,
133 item_imgs,
134 fonts,
135 common: widget::CommonBuilder::default(),
136 item_tooltip_manager,
137 slot_manager,
138 pulse,
139 menu_events,
140 active_content,
141 localized_strings,
142 item_i18n,
143 show_stats,
144 show_bag_inv,
145 on_right,
146 global_state,
147 item_tooltip,
148 playername,
149 entity,
150 is_us,
151 inventory,
152 bg_ids,
153 show_salvage,
154 details_mode,
155 }
156 }
157
158 fn background(&mut self, ui: &mut UiCell<'_>) {
159 let bag_settings = &self.global_state.settings.hud_position;
160 let bag_pos = if !self.on_right {
161 bag_settings.bag.other
162 } else {
163 bag_settings.bag.own
164 };
165
166 let bg_id = if !self.on_right {
167 self.imgs.inv_bg_bag
168 } else {
169 self.imgs.player_inv_bg_bag
170 };
171
172 let img_id = if !self.on_right {
173 self.imgs.inv_frame_bag
174 } else {
175 self.imgs.player_inv_frame_bag
176 };
177
178 let mut bg = Image::new(if self.show_stats {
179 self.imgs.inv_bg_stats
180 } else if self.show_bag_inv {
181 bg_id
182 } else {
183 self.imgs.inv_bg_armor
184 })
185 .w_h(
186 424.0,
187 if self.show_bag_inv && !self.on_right {
188 548.0
189 } else {
190 708.0
191 },
192 );
193
194 if self.on_right {
195 bg = bg.bottom_right_with_margins_on(ui.window, bag_pos.y, bag_pos.x);
196 } else {
197 bg = bg.bottom_left_with_margins_on(ui.window, bag_pos.y, bag_pos.x);
198 }
199
200 bg.color(Some(UI_MAIN)).set(self.bg_ids.bg, ui);
201
202 Image::new(if self.show_bag_inv {
203 img_id
204 } else {
205 self.imgs.inv_frame
206 })
207 .w_h(
208 424.0,
209 if self.show_bag_inv && !self.on_right {
210 548.0
211 } else {
212 708.0
213 },
214 )
215 .middle_of(self.bg_ids.bg)
216 .color(Some(UI_HIGHLIGHT_0))
217 .set(self.bg_ids.bg_frame, ui);
218 }
219
220 fn title(&mut self, state: &ConrodState<'_, InventoryScrollerState>, ui: &mut UiCell<'_>) {
221 Text::new(
222 &self
223 .localized_strings
224 .get_msg_ctx("hud-bag-inventory", &i18n::fluent_args! {
225 "playername" => &*self.playername,
226 }),
227 )
228 .mid_top_with_margin_on(self.bg_ids.bg_frame, 9.0)
229 .font_id(self.fonts.cyri.conrod_id)
230 .font_size(self.fonts.cyri.scale(22))
231 .color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
232 .set(state.ids.inventory_title_bg, ui);
233 Text::new(
234 &self
235 .localized_strings
236 .get_msg_ctx("hud-bag-inventory", &i18n::fluent_args! {
237 "playername" => &*self.playername,
238 }),
239 )
240 .top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0)
241 .font_id(self.fonts.cyri.conrod_id)
242 .font_size(self.fonts.cyri.scale(22))
243 .color(TEXT_COLOR)
244 .set(state.ids.inventory_title, ui);
245 }
246
247 fn scrollbar_and_slots(
248 &mut self,
249 state: &mut ConrodState<'_, InventoryScrollerState>,
250 events: &mut Vec<InventoryScrollerEvent>,
251 ui: &mut UiCell<'_>,
252 ) {
253 if self.active_content == 1 {
256 for event in self.menu_events {
257 match *event {
258 MenuInput::LocalFocus => {
259 events.push(InventoryScrollerEvent::ChangeLocalFocus(2));
260 },
261 MenuInput::Apply => {
262 },
264 MenuInput::Back => {
265 events.push(InventoryScrollerEvent::Close);
266 },
267 _ => {},
268 }
269 }
270 }
271
272 let space_max = self.inventory.slots().count();
273
274 if space_max > 45 && !self.show_bag_inv {
276 Image::new(self.imgs.scrollbar_bg)
278 .w_h(9.0, 173.0)
279 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
280 .color(Some(UI_HIGHLIGHT_0))
281 .set(state.ids.scrollbar_bg, ui);
282 Scrollbar::y_axis(state.ids.inv_alignment)
284 .thickness(5.0)
285 .h(123.0)
286 .color(UI_MAIN)
287 .middle_of(state.ids.scrollbar_bg)
288 .set(state.ids.scrollbar_slots, ui);
289 } else if space_max > 135 && self.on_right {
290 Image::new(self.imgs.scrollbar_bg_big)
292 .w_h(9.0, 592.0)
293 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
294 .color(Some(UI_HIGHLIGHT_0))
295 .set(state.ids.scrollbar_bg, ui);
296 Scrollbar::y_axis(state.ids.inv_alignment)
298 .thickness(5.0)
299 .h(542.0)
300 .color(UI_MAIN)
301 .middle_of(state.ids.scrollbar_bg)
302 .set(state.ids.scrollbar_slots, ui);
303 };
304
305 if space_max >= 108 && !self.on_right && self.show_bag_inv {
307 Image::new(self.imgs.second_phase_scrollbar_bg)
309 .w_h(9.0, 434.0)
310 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
311 .color(Some(UI_HIGHLIGHT_0))
312 .set(state.ids.second_phase_scrollbar_bg, ui);
313 Scrollbar::y_axis(state.ids.inv_alignment)
315 .thickness(5.0)
316 .h(384.0)
317 .color(UI_MAIN)
318 .middle_of(state.ids.second_phase_scrollbar_bg)
319 .set(state.ids.left_scrollbar_slots, ui);
320 }
321
322 let grid_width = 362.0;
323 let grid_height = if self.show_bag_inv && !self.on_right {
324 440.0 } else if self.show_bag_inv && self.on_right {
326 600.0 } else {
328 200.0
329 };
330
331 Rectangle::fill_with([grid_width, grid_height], color::TRANSPARENT)
333 .bottom_left_with_margins_on(
334 self.bg_ids.bg_frame,
335 29.0,
336 if self.show_bag_inv && !self.on_right {
337 28.0
338 } else {
339 46.5
340 },
341 )
342 .scroll_kids_vertically()
343 .set(state.ids.inv_alignment, ui);
344
345 for event in SlotGrid::new(
348 self.client,
349 self.imgs,
350 self.item_imgs,
351 self.fonts,
352 self.item_tooltip_manager,
353 self.slot_manager,
354 self.inventory,
355 self.item_tooltip,
356 self.localized_strings,
357 self.item_i18n,
358 self.entity,
359 &self.global_state.window.last_input(),
360 self.pulse,
361 self.menu_events,
362 self.active_content,
363 )
364 .columns(9) .spacing(0.0)
366 .is_us(self.is_us)
367 .details_mode(self.details_mode)
368 .show_salvage(self.show_salvage)
369 .slot_size(if self.details_mode { 20.0 } else { 40.0 }) .wh_of(state.ids.inv_alignment)
371 .top_left_of(state.ids.inv_alignment)
372 .set(state.ids.slot_grid, ui)
373 {
374 match event {
375 super::slot_grid::SlotEvents::ChangeLocalFocus(change) => {
376 events.push(InventoryScrollerEvent::ChangeLocalFocus(change));
377 },
378 super::slot_grid::SlotEvents::Close => {
379 events.push(InventoryScrollerEvent::Close);
380 },
381 }
382 }
383 }
384
385 fn footer_metrics(
386 &mut self,
387 state: &ConrodState<'_, InventoryScrollerState>,
388 ui: &mut UiCell<'_>,
389 ) {
390 let space_used = self.inventory.populated_slots();
391 let space_max = self.inventory.slots().count();
392 let bag_space = format!("{}/{}", space_used, space_max);
393 let bag_space_percentage = space_used as f32 / space_max as f32;
394 Text::new(&bag_space)
427 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 6.0, 43.0)
428 .font_id(self.fonts.cyri.conrod_id)
429 .font_size(self.fonts.cyri.scale(14))
430 .color(if bag_space_percentage < 0.8 {
431 TEXT_COLOR
432 } else if bag_space_percentage < 1.0 {
433 LOW_HP_COLOR
434 } else {
435 CRITICAL_HP_COLOR
436 })
437 .set(state.ids.space_txt, ui);
438 }
439
440 fn draggable_area(
441 &self,
442 state: &ConrodState<'_, InventoryScrollerState>,
443 events: &mut Vec<InventoryScrollerEvent>,
444 ui: &mut UiCell<'_>,
445 ) {
446 let bag_settings = &self.global_state.settings.hud_position;
447 let bag_pos = if !self.on_right {
448 bag_settings.bag.other
449 } else {
450 bag_settings.bag.own
451 };
452
453 let bag_size: Vec2<f64> = if !self.on_right {
454 [DEFAULT_OTHER_BAG_WIDTH, DEFAULT_OTHER_BAG_HEIGHT].into()
455 } else {
456 [DEFAULT_OWN_BAG_WIDTH, DEFAULT_OWN_BAG_HEIGHT].into()
457 };
458
459 let pos_delta: Vec2<f64> = ui
460 .widget_input(state.ids.draggable_area)
461 .drags()
462 .left()
463 .map(|drag| Vec2::<f64>::from(drag.delta_xy))
464 .sum();
465
466 let pos_delta: Vec2<f64> = if !self.on_right {
467 pos_delta
469 } else {
470 pos_delta.with_x(-pos_delta.x)
474 };
475
476 let window_clamp = Vec2::new(ui.win_w, ui.win_h) - bag_size;
477
478 let new_pos = (bag_pos + pos_delta)
479 .map(|e| e.max(0.))
480 .map2(window_clamp, |e, bounds| e.min(bounds));
481
482 if new_pos.abs_diff_ne(&bag_pos, f64::EPSILON) {
483 events.push(InventoryScrollerEvent::Drag(new_pos));
484 }
485
486 if ui
487 .widget_input(state.ids.draggable_area)
488 .clicks()
489 .right()
490 .count()
491 == 1
492 {
493 events.push(InventoryScrollerEvent::Drag(if self.on_right {
494 HudPositionSettings::default().bag.own
495 } else {
496 HudPositionSettings::default().bag.other
497 }));
498 }
499
500 Rectangle::fill_with([424.0, 48.0], color::TRANSPARENT)
501 .top_left_with_margin_on(self.bg_ids.bg_frame, 0.0)
502 .set(state.ids.draggable_area, ui);
503 }
504}
505
506impl Widget for InventoryScroller<'_> {
507 type Event = Vec<InventoryScrollerEvent>;
508 type State = InventoryScrollerState;
509 type Style = ();
510
511 fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
512 InventoryScrollerState {
513 ids: InventoryScrollerIds::new(id_gen),
514 }
515 }
516
517 fn style(&self) -> Self::Style {}
518
519 fn update(mut self, args: widget::UpdateArgs<Self>) -> Self::Event {
520 let widget::UpdateArgs { state, ui, .. } = args;
521 let mut events = Vec::new();
522 self.background(ui);
523 self.title(state, ui);
524 self.scrollbar_and_slots(state, &mut events, ui);
525 self.footer_metrics(state, ui);
526 if self
527 .global_state
528 .settings
529 .interface
530 .toggle_draggable_windows
531 {
532 self.draggable_area(state, &mut events, ui);
533 }
534 events
535 }
536}
537
538widget_ids! {
539 pub struct BackgroundIds {
540 bg,
541 bg_frame,
542 }
543}
544
545widget_ids! {
546 pub struct BagIds {
547 inventory_scroller,
548 bag_close,
549 char_ico,
550 inventory_sort,
551 inventory_sort_selected,
552 bag_expand_btn,
553 bag_details_btn,
554 head_slot,
556 neck_slot,
557 chest_slot,
558 shoulders_slot,
559 hands_slot,
560 legs_slot,
561 belt_slot,
562 lantern_slot,
563 ring1_slot,
564 ring2_slot,
565 feet_slot,
566 back_slot,
567 tabard_slot,
568 glider_slot,
569 active_mainhand_slot,
570 active_offhand_slot,
571 inactive_mainhand_slot,
572 inactive_offhand_slot,
573 swap_equipped_weapons_btn,
574 bag1_slot,
575 bag2_slot,
576 bag3_slot,
577 bag4_slot,
578 stat_icons[],
580 stat_txts[],
581 }
582}
583
584#[derive(WidgetCommon)]
585pub struct Bag<'a> {
586 client: &'a Client,
587 info: &'a HudInfo<'a>,
588 global_state: &'a GlobalState,
589 imgs: &'a Imgs,
590 item_imgs: &'a ItemImgs,
591 fonts: &'a Fonts,
592 #[conrod(common_builder)]
593 common: widget::CommonBuilder,
594 rot_imgs: &'a ImgsRot,
595 tooltip_manager: &'a mut TooltipManager,
596 item_tooltip_manager: &'a mut ItemTooltipManager,
597 slot_manager: &'a mut SlotManager,
598 pulse: f32,
599 localized_strings: &'a Localization,
600 item_i18n: &'a ItemI18n,
601 stats: &'a Stats,
602 skill_set: &'a SkillSet,
603 health: &'a Health,
604 energy: &'a Energy,
605 show: &'a Show,
606 body: &'a Body,
607 msm: &'a MaterialStatManifest,
608 rbm: &'a RecipeBookManifest,
609 poise: &'a Poise,
610 menu_events: &'a Vec<MenuInput>,
611}
612
613impl<'a> Bag<'a> {
614 #[expect(clippy::too_many_arguments)]
615 pub fn new(
616 client: &'a Client,
617 info: &'a HudInfo,
618 global_state: &'a GlobalState,
619 imgs: &'a Imgs,
620 item_imgs: &'a ItemImgs,
621 fonts: &'a Fonts,
622 rot_imgs: &'a ImgsRot,
623 tooltip_manager: &'a mut TooltipManager,
624 item_tooltip_manager: &'a mut ItemTooltipManager,
625 slot_manager: &'a mut SlotManager,
626 pulse: f32,
627 localized_strings: &'a Localization,
628 item_i18n: &'a ItemI18n,
629 stats: &'a Stats,
630 skill_set: &'a SkillSet,
631 health: &'a Health,
632 energy: &'a Energy,
633 show: &'a Show,
634 body: &'a Body,
635 msm: &'a MaterialStatManifest,
636 rbm: &'a RecipeBookManifest,
637 poise: &'a Poise,
638 menu_events: &'a Vec<MenuInput>,
639 ) -> Self {
640 Self {
641 client,
642 info,
643 global_state,
644 imgs,
645 item_imgs,
646 fonts,
647 common: widget::CommonBuilder::default(),
648 rot_imgs,
649 tooltip_manager,
650 item_tooltip_manager,
651 slot_manager,
652 pulse,
653 localized_strings,
654 item_i18n,
655 stats,
656 skill_set,
657 energy,
658 health,
659 show,
660 body,
661 msm,
662 rbm,
663 poise,
664 menu_events,
665 }
666 }
667}
668const STATS: [&str; 6] = [
669 "Health",
670 "Energy",
671 "Protection",
672 "Combat Rating",
673 "Stun Resilience",
674 "Stealth",
675];
676
677pub struct BagState {
678 ids: BagIds,
679 bg_ids: BackgroundIds,
680
681 active_content: usize,
682 active_gear_slot: usize,
683}
684
685pub enum Event {
686 BagExpand,
687 Close,
688 ChangeInventorySortOrder(InventorySortOrder),
689 SortInventory(InventorySortOrder),
690 SwapEquippedWeapons,
691 SetDetailsMode(bool),
692 MoveBag(Vec2<f64>),
693}
694
695impl Widget for Bag<'_> {
696 type Event = Vec<Event>;
697 type State = BagState;
698 type Style = ();
699
700 fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
701 BagState {
702 bg_ids: BackgroundIds {
703 bg: id_gen.next(),
704 bg_frame: id_gen.next(),
705 },
706 ids: BagIds::new(id_gen),
707 active_content: 0,
708 active_gear_slot: 1,
709 }
710 }
711
712 fn style(&self) -> Self::Style {}
713
714 fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
715 common_base::prof_span!("Bag::update");
716 let widget::UpdateArgs { state, ui, .. } = args;
717 let i18n = &self.localized_strings;
718
719 let mut events = Vec::new();
720
721 let mut change_local_focus = false;
726
727 if state.active_content == 2 {
737 for event in self.menu_events {
738 match *event {
739 MenuInput::LocalFocus => {
740 change_local_focus = true;
743 },
744 MenuInput::Up => state.update(|s| {
745 match s.active_gear_slot {
747 0 => {},
749 1 => s.active_gear_slot = 5,
751 2 => s.active_gear_slot = 6,
753 3 => s.active_gear_slot = 6,
755 4 => s.active_gear_slot = 7,
757 5 => s.active_gear_slot = 8,
759 6 => s.active_gear_slot = 9,
761 7 => s.active_gear_slot = 10,
763 8 => s.active_gear_slot = 11,
765 9 => s.active_gear_slot = 12,
767 10 => s.active_gear_slot = 13,
769 11 => s.active_gear_slot = 14,
771 12 => s.active_gear_slot = 14,
773 13 => s.active_gear_slot = 14,
775 14 => s.active_gear_slot = 15,
777 15 => {},
779 16 => s.active_gear_slot = 17,
781 17 => s.active_gear_slot = 18,
783 18 => {},
785 _ => s.active_gear_slot = 0,
787 }
788 }),
789 MenuInput::Down => state.update(|s| {
790 match s.active_gear_slot {
791 0 => {},
793 1 => {},
795 2 => {},
797 3 => {},
799 4 => {},
801 5 => s.active_gear_slot = 1,
803 6 => s.active_gear_slot = 2,
805 7 => s.active_gear_slot = 4,
807 8 => s.active_gear_slot = 5,
809 9 => s.active_gear_slot = 6,
811 10 => s.active_gear_slot = 7,
813 11 => s.active_gear_slot = 8,
815 12 => s.active_gear_slot = 9,
817 13 => s.active_gear_slot = 10,
819 14 => s.active_gear_slot = 12,
821 15 => s.active_gear_slot = 14,
823 16 => {},
825 17 => s.active_gear_slot = 16,
827 18 => s.active_gear_slot = 17,
829 _ => s.active_gear_slot = 0,
831 }
832 }),
833 MenuInput::Left => state.update(|s| {
834 match s.active_gear_slot {
835 0 => {},
837 1 => s.active_gear_slot = 0 + 1,
839 2 => s.active_gear_slot = 1,
841 3 => s.active_gear_slot = 2,
843 4 => s.active_gear_slot = 3,
845 5 => {},
847 6 => s.active_gear_slot = 5,
849 7 => s.active_gear_slot = 6,
851 8 => {},
853 9 => s.active_gear_slot = 8,
855 10 => s.active_gear_slot = 9,
857 11 => {},
859 12 => s.active_gear_slot = 11,
861 13 => s.active_gear_slot = 12,
863 14 => {},
865 15 => {},
867 16 => s.active_gear_slot = 13,
869 17 => s.active_gear_slot = 14,
871 18 => s.active_gear_slot = 15,
873 _ => s.active_gear_slot = 0,
875 }
876 }),
877 MenuInput::Right => state.update(|s| {
878 match s.active_gear_slot {
879 0 => s.active_gear_slot = 1,
881 1 => s.active_gear_slot = 2,
883 2 => s.active_gear_slot = 3,
885 3 => s.active_gear_slot = 4,
887 4 => s.active_gear_slot = 16,
889 5 => s.active_gear_slot = 6,
891 6 => s.active_gear_slot = 7,
893 7 => s.active_gear_slot = 16,
895 8 => s.active_gear_slot = 9,
897 9 => s.active_gear_slot = 10,
899 10 => s.active_gear_slot = 16,
901 11 => s.active_gear_slot = 12,
903 12 => s.active_gear_slot = 13,
905 13 => s.active_gear_slot = 16,
907 14 => s.active_gear_slot = 17,
909 15 => s.active_gear_slot = 18,
911 16 => {},
913 17 => {},
915 18 => {},
917 _ => s.active_gear_slot = 0,
919 }
920 }),
921 MenuInput::Apply => {
922 },
924 MenuInput::Back => {
925 events.push(Event::Close);
928 },
929 _ => {},
930 }
931 }
932 }
933
934 let bag_tooltip = Tooltip::new({
935 let edge = &self.rot_imgs.tt_side;
938 let corner = &self.rot_imgs.tt_corner;
939 ImageFrame::new(
940 [edge.cw180, edge.none, edge.cw270, edge.cw90],
941 [corner.none, corner.cw270, corner.cw90, corner.cw180],
942 Color::Rgba(0.08, 0.07, 0.04, 1.0),
943 5.0,
944 )
945 })
946 .title_font_size(self.fonts.cyri.scale(15))
947 .parent(ui.window)
948 .desc_font_size(self.fonts.cyri.scale(12))
949 .font_id(self.fonts.cyri.conrod_id)
950 .desc_text_color(TEXT_COLOR);
951
952 if let Some(inventory) = self.client.inventories().get(self.info.viewpoint_entity) {
953 let tooltip = Tooltip::new({
955 let edge = &self.rot_imgs.tt_side;
958 let corner = &self.rot_imgs.tt_corner;
959 ImageFrame::new(
960 [edge.cw180, edge.none, edge.cw270, edge.cw90],
961 [corner.none, corner.cw270, corner.cw90, corner.cw180],
962 Color::Rgba(0.08, 0.07, 0.04, 1.0),
963 5.0,
964 )
965 })
966 .title_font_size(self.fonts.cyri.scale(15))
967 .parent(ui.window)
968 .desc_font_size(self.fonts.cyri.scale(12))
969 .font_id(self.fonts.cyri.conrod_id)
970 .desc_text_color(TEXT_COLOR);
971
972 let item_tooltip = ItemTooltip::new(
973 {
974 let edge = &self.rot_imgs.tt_side;
977 let corner = &self.rot_imgs.tt_corner;
978 ImageFrame::new(
979 [edge.cw180, edge.none, edge.cw270, edge.cw90],
980 [corner.none, corner.cw270, corner.cw90, corner.cw180],
981 Color::Rgba(0.08, 0.07, 0.04, 1.0),
982 5.0,
983 )
984 },
985 self.client,
986 self.info,
987 self.imgs,
988 self.item_imgs,
989 self.pulse,
990 self.msm,
991 self.rbm,
992 Some(inventory),
993 self.localized_strings,
994 self.item_i18n,
995 )
996 .title_font_size(self.fonts.cyri.scale(20))
997 .parent(ui.window)
998 .desc_font_size(self.fonts.cyri.scale(12))
999 .font_id(self.fonts.cyri.conrod_id)
1000 .desc_text_color(TEXT_COLOR);
1001
1002 for event in InventoryScroller::new(
1003 self.client,
1004 self.global_state,
1005 self.imgs,
1006 self.item_imgs,
1007 self.fonts,
1008 self.item_tooltip_manager,
1009 self.slot_manager,
1010 self.pulse,
1011 self.menu_events,
1012 state.active_content,
1013 self.localized_strings,
1014 self.item_i18n,
1015 self.show.stats,
1016 self.show.bag_inv,
1017 true,
1018 &item_tooltip,
1019 self.localized_strings.get_content(&self.stats.name),
1020 self.info.viewpoint_entity,
1021 true,
1022 inventory,
1023 &state.bg_ids,
1024 self.show.crafting_fields.salvage,
1025 self.show.bag_details,
1026 )
1027 .set(state.ids.inventory_scroller, ui)
1028 {
1029 match event {
1030 InventoryScrollerEvent::Drag(pos) => {
1031 events.push(Event::MoveBag(pos));
1032 },
1033 InventoryScrollerEvent::ChangeLocalFocus(change) => state.update(|s| {
1034 s.active_content = change;
1035 }),
1036 InventoryScrollerEvent::Close => {
1037 events.push(Event::Close);
1038 },
1039 }
1040 }
1041
1042 if change_local_focus {
1045 state.update(|s| {
1046 s.active_content = 0;
1047 })
1048 }
1049
1050 Image::new(self.imgs.char_art)
1052 .w_h(40.0, 37.0)
1053 .top_left_with_margins_on(state.bg_ids.bg, 4.0, 2.0)
1054 .set(state.ids.char_ico, ui);
1055
1056 let buttons_top = if self.show.bag_inv { 53.0 } else { 460.0 };
1057 let (txt, btn, hover, press) = if self.show.bag_details {
1058 (
1059 "Grid mode",
1060 self.imgs.grid_btn,
1061 self.imgs.grid_btn_hover,
1062 self.imgs.grid_btn_press,
1063 )
1064 } else {
1065 (
1066 "List mode",
1067 self.imgs.list_btn,
1068 self.imgs.list_btn_hover,
1069 self.imgs.list_btn_press,
1070 )
1071 };
1072 let details_btn = Button::image(btn)
1073 .w_h(32.0, 17.0)
1074 .hover_image(hover)
1075 .press_image(press);
1076 if details_btn
1077 .mid_top_with_margin_on(state.bg_ids.bg_frame, buttons_top)
1078 .with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
1079 .set(state.ids.bag_details_btn, ui)
1080 .was_clicked()
1081 {
1082 events.push(Event::SetDetailsMode(!self.show.bag_details));
1083 }
1084 let (txt, btn, hover, press) = if self.show.bag_inv {
1086 (
1087 "Show Loadout",
1088 self.imgs.collapse_btn,
1089 self.imgs.collapse_btn_hover,
1090 self.imgs.collapse_btn_press,
1091 )
1092 } else {
1093 (
1094 "Expand Bag",
1095 self.imgs.expand_btn,
1096 self.imgs.expand_btn_hover,
1097 self.imgs.expand_btn_press,
1098 )
1099 };
1100 let expand_btn = Button::image(btn)
1101 .w_h(30.0, 17.0)
1102 .hover_image(hover)
1103 .press_image(press);
1104
1105 if (inventory.slots().count() > 45 || self.show.bag_inv)
1107 && expand_btn
1108 .top_right_with_margins_on(state.bg_ids.bg_frame, buttons_top, 37.0)
1109 .with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
1110 .set(state.ids.bag_expand_btn, ui)
1111 .was_clicked()
1112 {
1113 events.push(Event::BagExpand);
1114 }
1115
1116 if Button::image(self.imgs.inv_sort_btn)
1118 .w_h(30.0, 17.0)
1119 .hover_image(self.imgs.inv_sort_btn_hover)
1120 .press_image(self.imgs.inv_sort_btn_press)
1121 .top_left_with_margins_on(state.bg_ids.bg_frame, buttons_top, 87.0) .with_tooltip(
1123 self.tooltip_manager,
1124 &(match self.global_state.settings.inventory.sort_order.next() {
1125 InventorySortOrder::Name => i18n.get_msg("hud-bag-change_to_sort_by_name"),
1126 InventorySortOrder::Quality => i18n.get_msg("hud-bag-change_to_sort_by_quality"),
1127 InventorySortOrder::Category => i18n.get_msg("hud-bag-change_to_sort_by_category"),
1128 InventorySortOrder::Tag => i18n.get_msg("hud-bag-change_to_sort_by_tag"),
1129 InventorySortOrder::Amount => i18n.get_msg("hud-bag-change_to_sort_by_quantity"),
1130 }),
1131 "",
1132 &tooltip,
1133 color::WHITE,
1134 )
1135 .set(state.ids.inventory_sort, ui)
1136 .was_clicked()
1137 {
1138 events.push(Event::ChangeInventorySortOrder(
1140 self.global_state.settings.inventory.sort_order.next(),
1141 ));
1142 }
1143 if Button::image(self.imgs.inv_sort_selected_btn)
1145 .w_h(30.0, 17.0)
1146 .hover_image(self.imgs.inv_sort_selected_btn_hover)
1147 .press_image(self.imgs.inv_sort_selected_btn_press)
1148 .top_left_with_margins_on(state.bg_ids.bg_frame, buttons_top, 47.0)
1149 .with_tooltip(
1150 self.tooltip_manager,
1151 &(match self.global_state.settings.inventory.sort_order {
1152 InventorySortOrder::Name => i18n.get_msg("hud-bag-sort_by_name"),
1153 InventorySortOrder::Quality => i18n.get_msg("hud-bag-sort_by_quality"),
1154 InventorySortOrder::Category => i18n.get_msg("hud-bag-sort_by_category"),
1155 InventorySortOrder::Tag => i18n.get_msg("hud-bag-sort_by_tag"),
1156 InventorySortOrder::Amount => i18n.get_msg("hud-bag-sort_by_quantity"),
1157 }),
1158 "",
1159 &tooltip,
1160 color::WHITE,
1161 )
1162 .set(state.ids.inventory_sort_selected, ui)
1163 .was_clicked()
1164 {
1165 events.push(Event::SortInventory(
1166 self.global_state.settings.inventory.sort_order,
1167 ));
1168 }
1169
1170 let mut slot_maker = SlotMaker {
1172 empty_slot: self.imgs.armor_slot_empty,
1173 hovered_slot: self.imgs.skillbar_index,
1174 filled_slot: self.imgs.armor_slot,
1175 selected_slot: self.imgs.armor_slot_sel,
1176 background_color: Some(UI_HIGHLIGHT_0),
1177 content_size: ContentSize {
1178 width_height_ratio: 1.0,
1179 max_fraction: 0.75, },
1183 selected_content_scale: 1.067,
1184 amount_font: self.fonts.cyri.conrod_id,
1185 amount_margins: Vec2::new(-4.0, 0.0),
1186 amount_font_size: self.fonts.cyri.scale(12),
1187 amount_text_color: TEXT_COLOR,
1188 content_source: inventory,
1189 image_source: self.item_imgs,
1190 slot_manager: Some(self.slot_manager),
1191 last_input: &self.global_state.window.last_input(),
1192 pulse: self.pulse,
1193 };
1194
1195 macro_rules! set_tooltip {
1216 ($slot_maker:expr, $slot_id:expr, $slot:expr, $desc:expr) => {
1217 if let Some(item) = inventory.equipped($slot) {
1218 let manager = &mut *self.item_tooltip_manager;
1219 $slot_maker
1220 .with_item_tooltip(
1221 manager,
1222 core::iter::once(item as &dyn ItemDesc),
1223 &None,
1224 &item_tooltip,
1225 )
1226 .set($slot_id, ui)
1227 } else {
1228 let manager = &mut *self.tooltip_manager;
1229 $slot_maker
1230 .with_tooltip(manager, &i18n.get_msg($desc), "", &tooltip, color::WHITE)
1231 .set($slot_id, ui)
1232 }
1233 };
1234 }
1235
1236 let filled_slot = self.imgs.armor_slot;
1237 if !self.show.bag_inv {
1238 state.update(|s| {
1240 s.ids
1241 .stat_icons
1242 .resize(STATS.len(), &mut ui.widget_id_generator())
1243 });
1244 state.update(|s| {
1245 s.ids
1246 .stat_txts
1247 .resize(STATS.len(), &mut ui.widget_id_generator())
1248 });
1249 let combat_rating = combat_rating(
1251 inventory,
1252 self.health,
1253 self.energy,
1254 self.poise,
1255 self.skill_set,
1256 *self.body,
1257 self.msm,
1258 )
1259 .min(999.9);
1260 let indicator_col = cr_color(combat_rating);
1261 for i in STATS.iter().copied().enumerate() {
1262 let btn = Button::image(match i.1 {
1263 "Health" => self.imgs.health_ico,
1264 "Energy" => self.imgs.energy_ico,
1265 "Combat Rating" => self.imgs.combat_rating_ico,
1266 "Protection" => self.imgs.protection_ico,
1267 "Stun Resilience" => self.imgs.stun_res_ico,
1268 "Stealth" => self.imgs.stealth_rating_ico,
1269 _ => self.imgs.nothing,
1270 })
1271 .w_h(20.0, 20.0)
1272 .image_color(if i.1 == "Combat Rating" {
1273 indicator_col
1274 } else {
1275 TEXT_COLOR
1276 });
1277 let protection_txt = format!(
1278 "{}%",
1279 (100.0
1280 * Damage::compute_damage_reduction(
1281 None,
1282 Some(inventory),
1283 Some(self.stats),
1284 self.msm
1285 )) as i32
1286 );
1287 let health_txt = format!("{}", self.health.maximum().round() as usize);
1288 let energy_txt = format!("{}", self.energy.maximum().round() as usize);
1289 let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
1290 let stun_res_txt = format!(
1291 "{}",
1292 (100.0
1293 * Poise::compute_poise_damage_reduction(
1294 Some(inventory),
1295 self.msm,
1296 None,
1297 Some(self.stats),
1298 )) as i32
1299 );
1300 let stealth_txt = format!(
1301 "{:.1}%",
1302 ((1.0
1303 - perception_dist_multiplier_from_stealth(
1304 Some(inventory),
1305 None,
1306 self.msm
1307 ))
1308 * 100.0)
1309 );
1310 let btn = if i.0 == 0 {
1311 btn.top_left_with_margins_on(state.bg_ids.bg_frame, 55.0, 10.0)
1312 } else {
1313 btn.down_from(state.ids.stat_icons[i.0 - 1], 7.0)
1314 };
1315 let tooltip_head = match i.1 {
1316 "Health" => i18n.get_msg("hud-bag-health"),
1317 "Energy" => i18n.get_msg("hud-bag-energy"),
1318 "Combat Rating" => i18n.get_msg("hud-bag-combat_rating"),
1319 "Protection" => i18n.get_msg("hud-bag-protection"),
1320 "Stun Resilience" => i18n.get_msg("hud-bag-stun_res"),
1321 "Stealth" => i18n.get_msg("hud-bag-stealth"),
1322 _ => Cow::Borrowed(""),
1323 };
1324 let tooltip_txt = match i.1 {
1325 "Combat Rating" => i18n.get_msg("hud-bag-combat_rating_desc"),
1326 "Protection" => i18n.get_msg("hud-bag-protection_desc"),
1327 "Stun Resilience" => i18n.get_msg("hud-bag-stun_res_desc"),
1328 _ => Cow::Borrowed(""),
1329 };
1330 btn.with_tooltip(
1331 self.tooltip_manager,
1332 &tooltip_head,
1333 &tooltip_txt,
1334 &bag_tooltip,
1335 TEXT_COLOR,
1336 )
1337 .set(state.ids.stat_icons[i.0], ui);
1338 Text::new(match i.1 {
1339 "Health" => &health_txt,
1340 "Energy" => &energy_txt,
1341 "Combat Rating" => &combat_rating_txt,
1342 "Protection" => &protection_txt,
1343 "Stun Resilience" => &stun_res_txt,
1344 "Stealth" => &stealth_txt,
1345 _ => "",
1346 })
1347 .right_from(state.ids.stat_icons[i.0], 10.0)
1348 .font_id(self.fonts.cyri.conrod_id)
1349 .font_size(self.fonts.cyri.scale(14))
1350 .color(TEXT_COLOR)
1351 .graphics_for(state.ids.stat_icons[i.0])
1352 .set(state.ids.stat_txts[i.0], ui);
1353 }
1354 let item_slot = EquipSlot::Armor(ArmorSlot::Head);
1357 let slot = slot_maker
1358 .fabricate(
1359 item_slot,
1360 [45.0; 2],
1361 state.active_gear_slot == 15 && state.active_content == 2,
1362 false,
1363 )
1364 .mid_top_with_margin_on(state.bg_ids.bg_frame, 60.0)
1365 .with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
1366 .filled_slot(filled_slot);
1367
1368 let slot_id = state.ids.head_slot;
1369 set_tooltip!(slot, slot_id, item_slot, "hud-bag-head");
1370
1371 let item_slot = EquipSlot::Armor(ArmorSlot::Neck);
1373 let slot = slot_maker
1374 .fabricate(
1375 item_slot,
1376 [45.0; 2],
1377 state.active_gear_slot == 14 && state.active_content == 2,
1378 false,
1379 )
1380 .mid_bottom_with_margin_on(state.ids.head_slot, -55.0)
1381 .with_icon(self.imgs.necklace_bg, Vec2::new(40.0, 31.0), Some(UI_MAIN))
1382 .filled_slot(filled_slot);
1383
1384 let slot_id = state.ids.neck_slot;
1385 set_tooltip!(slot, slot_id, item_slot, "hud-bag-neck");
1386
1387 let item_slot = EquipSlot::Armor(ArmorSlot::Chest);
1390 let slot = slot_maker
1391 .fabricate(
1392 item_slot,
1393 [85.0; 2],
1394 state.active_gear_slot == 12 && state.active_content == 2,
1395 false,
1396 )
1397 .mid_bottom_with_margin_on(state.ids.neck_slot, -95.0)
1398 .with_icon(self.imgs.chest_bg, Vec2::new(64.0, 42.0), Some(UI_MAIN))
1399 .filled_slot(filled_slot);
1400
1401 let slot_id = state.ids.chest_slot;
1402 set_tooltip!(slot, slot_id, item_slot, "hud-bag-chest");
1403
1404 let item_slot = EquipSlot::Armor(ArmorSlot::Shoulders);
1406 let slot = slot_maker
1407 .fabricate(
1408 item_slot,
1409 [70.0; 2],
1410 state.active_gear_slot == 11 && state.active_content == 2,
1411 false,
1412 )
1413 .bottom_left_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
1414 .with_icon(self.imgs.shoulders_bg, Vec2::new(60.0, 36.0), Some(UI_MAIN))
1415 .filled_slot(filled_slot);
1416
1417 let slot_id = state.ids.shoulders_slot;
1418 set_tooltip!(slot, slot_id, item_slot, "hud-bag-shoulders");
1419
1420 let item_slot = EquipSlot::Armor(ArmorSlot::Hands);
1422 let slot = slot_maker
1423 .fabricate(
1424 item_slot,
1425 [70.0; 2],
1426 state.active_gear_slot == 13 && state.active_content == 2,
1427 false,
1428 )
1429 .bottom_right_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
1430 .with_icon(self.imgs.hands_bg, Vec2::new(55.0, 60.0), Some(UI_MAIN))
1431 .filled_slot(filled_slot);
1432
1433 let slot_id = state.ids.hands_slot;
1434 set_tooltip!(slot, slot_id, item_slot, "hud-bag-hands");
1435
1436 let item_slot = EquipSlot::Armor(ArmorSlot::Belt);
1438 let slot = slot_maker
1439 .fabricate(
1440 item_slot,
1441 [45.0; 2],
1442 state.active_gear_slot == 9 && state.active_content == 2,
1443 false,
1444 )
1445 .mid_bottom_with_margin_on(state.ids.chest_slot, -55.0)
1446 .with_icon(self.imgs.belt_bg, Vec2::new(40.0, 23.0), Some(UI_MAIN))
1447 .filled_slot(filled_slot);
1448
1449 let slot_id = state.ids.belt_slot;
1450 set_tooltip!(slot, slot_id, item_slot, "hud-bag-belt");
1451
1452 let item_slot = EquipSlot::Armor(ArmorSlot::Legs);
1454 let slot = slot_maker
1455 .fabricate(
1456 item_slot,
1457 [85.0; 2],
1458 state.active_gear_slot == 6 && state.active_content == 2,
1459 false,
1460 )
1461 .mid_bottom_with_margin_on(state.ids.belt_slot, -95.0)
1462 .with_icon(self.imgs.legs_bg, Vec2::new(48.0, 70.0), Some(UI_MAIN))
1463 .filled_slot(filled_slot);
1464
1465 let slot_id = state.ids.legs_slot;
1466 set_tooltip!(slot, slot_id, item_slot, "hud-bag-legs");
1467
1468 let item_slot = EquipSlot::Armor(ArmorSlot::Ring1);
1470 let slot = slot_maker
1471 .fabricate(
1472 item_slot,
1473 [45.0; 2],
1474 state.active_gear_slot == 10 && state.active_content == 2,
1475 false,
1476 )
1477 .bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0)
1478 .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
1479 .filled_slot(filled_slot);
1480
1481 let slot_id = state.ids.ring1_slot;
1482 set_tooltip!(slot, slot_id, item_slot, "hud-bag-ring");
1483
1484 let item_slot = EquipSlot::Armor(ArmorSlot::Ring2);
1486 let slot = slot_maker
1487 .fabricate(
1488 item_slot,
1489 [45.0; 2],
1490 state.active_gear_slot == 8 && state.active_content == 2,
1491 false,
1492 )
1493 .bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0)
1494 .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
1495 .filled_slot(filled_slot);
1496
1497 let slot_id = state.ids.ring2_slot;
1498 set_tooltip!(slot, slot_id, item_slot, "hud-bag-ring");
1499
1500 let item_slot = EquipSlot::Armor(ArmorSlot::Back);
1502 let slot = slot_maker
1503 .fabricate(
1504 item_slot,
1505 [45.0; 2],
1506 state.active_gear_slot == 5 && state.active_content == 2,
1507 false,
1508 )
1509 .down_from(state.ids.ring2_slot, 10.0)
1510 .with_icon(self.imgs.back_bg, Vec2::new(33.0, 40.0), Some(UI_MAIN))
1511 .filled_slot(filled_slot);
1512
1513 let slot_id = state.ids.back_slot;
1514 set_tooltip!(slot, slot_id, item_slot, "hud-bag-back");
1515
1516 let item_slot = EquipSlot::Armor(ArmorSlot::Feet);
1518 let slot = slot_maker
1519 .fabricate(
1520 item_slot,
1521 [45.0; 2],
1522 state.active_gear_slot == 7 && state.active_content == 2,
1523 false,
1524 )
1525 .down_from(state.ids.ring1_slot, 10.0)
1526 .with_icon(self.imgs.feet_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
1527 .filled_slot(filled_slot);
1528
1529 let slot_id = state.ids.feet_slot;
1530 set_tooltip!(slot, slot_id, item_slot, "hud-bag-feet");
1531
1532 let item_slot = EquipSlot::Lantern;
1534 let slot = slot_maker
1535 .fabricate(
1536 item_slot,
1537 [45.0; 2],
1538 state.active_gear_slot == 18 && state.active_content == 2,
1539 false,
1540 )
1541 .top_right_with_margins_on(state.bg_ids.bg_frame, 60.0, 5.0)
1542 .with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN))
1543 .filled_slot(filled_slot);
1544
1545 let slot_id = state.ids.lantern_slot;
1546 set_tooltip!(slot, slot_id, item_slot, "hud-bag-lantern");
1547
1548 let item_slot = EquipSlot::Glider;
1550 let slot = slot_maker
1551 .fabricate(
1552 item_slot,
1553 [45.0; 2],
1554 state.active_gear_slot == 17 && state.active_content == 2,
1555 false,
1556 )
1557 .down_from(state.ids.lantern_slot, 5.0)
1558 .with_icon(self.imgs.glider_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN))
1559 .filled_slot(filled_slot);
1560
1561 let slot_id = state.ids.glider_slot;
1562 set_tooltip!(slot, slot_id, item_slot, "hud-bag-glider");
1563
1564 let item_slot = EquipSlot::Armor(ArmorSlot::Tabard);
1566 let slot = slot_maker
1567 .fabricate(
1568 item_slot,
1569 [45.0; 2],
1570 state.active_gear_slot == 16 && state.active_content == 2,
1571 false,
1572 )
1573 .down_from(state.ids.glider_slot, 5.0)
1574 .with_icon(self.imgs.tabard_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN))
1575 .filled_slot(filled_slot);
1576
1577 let slot_id = state.ids.tabard_slot;
1578 set_tooltip!(slot, slot_id, item_slot, "hud-bag-tabard");
1579
1580 let item_slot = EquipSlot::ActiveMainhand;
1582 let slot = slot_maker
1583 .fabricate(
1584 item_slot,
1585 [85.0; 2],
1586 state.active_gear_slot == 1 && state.active_content == 2,
1587 false,
1588 )
1589 .bottom_right_with_margins_on(state.ids.back_slot, -95.0, 0.0)
1590 .with_icon(self.imgs.mainhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
1591 .filled_slot(filled_slot);
1592
1593 let slot_id = state.ids.active_mainhand_slot;
1594 set_tooltip!(slot, slot_id, item_slot, "hud-bag-mainhand");
1595
1596 let item_slot = EquipSlot::ActiveOffhand;
1598 let slot = slot_maker
1599 .fabricate(
1600 item_slot,
1601 [85.0; 2],
1602 state.active_gear_slot == 4 && state.active_content == 2,
1603 false,
1604 )
1605 .bottom_left_with_margins_on(state.ids.feet_slot, -95.0, 0.0)
1606 .with_icon(self.imgs.offhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
1607 .filled_slot(filled_slot);
1608
1609 let slot_id = state.ids.active_offhand_slot;
1610 set_tooltip!(slot, slot_id, item_slot, "hud-bag-offhand");
1611
1612 let item_slot = EquipSlot::InactiveMainhand;
1614 let slot = slot_maker
1615 .fabricate(
1616 item_slot,
1617 [40.0; 2],
1618 state.active_gear_slot == 2 && state.active_content == 2,
1619 false,
1620 )
1621 .bottom_right_with_margins_on(state.ids.active_mainhand_slot, 3.0, -47.0)
1622 .with_icon(self.imgs.mainhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
1623 .filled_slot(filled_slot);
1624
1625 let slot_id = state.ids.inactive_mainhand_slot;
1626 set_tooltip!(slot, slot_id, item_slot, "hud-bag-inactive_mainhand");
1627
1628 let item_slot = EquipSlot::InactiveOffhand;
1630 let slot = slot_maker
1631 .fabricate(
1632 item_slot,
1633 [40.0; 2],
1634 state.active_gear_slot == 3 && state.active_content == 2,
1635 false,
1636 )
1637 .bottom_left_with_margins_on(state.ids.active_offhand_slot, 3.0, -47.0)
1638 .with_icon(self.imgs.offhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
1639 .filled_slot(filled_slot);
1640
1641 let slot_id = state.ids.inactive_offhand_slot;
1642 set_tooltip!(slot, slot_id, item_slot, "hud-bag-inactive_offhand");
1643
1644 if Button::image(self.imgs.swap_equipped_weapons_btn)
1645 .hover_image(self.imgs.swap_equipped_weapons_btn_hover)
1646 .press_image(self.imgs.swap_equipped_weapons_btn_press)
1647 .w_h(32.0, 40.0)
1648 .bottom_left_with_margins_on(state.bg_ids.bg_frame, 0.0, 23.3)
1649 .align_middle_y_of(state.ids.active_mainhand_slot)
1650 .with_tooltip(
1651 self.tooltip_manager,
1652 &i18n.get_msg("hud-bag-swap_equipped_weapons_title"),
1653 &(if let Some(key) = self
1654 .global_state
1655 .settings
1656 .controls
1657 .get_binding(GameInput::SwapLoadout)
1658 {
1659 i18n.get_msg_ctx(
1660 "hud-bag-swap_equipped_weapons_desc",
1661 &i18n::fluent_args! {
1662 "key" => key.display_string()
1663 },
1664 )
1665 } else {
1666 Cow::Borrowed("")
1667 }),
1668 &tooltip,
1669 color::WHITE,
1670 )
1671 .set(state.ids.swap_equipped_weapons_btn, ui)
1672 .was_clicked()
1673 {
1674 events.push(Event::SwapEquippedWeapons);
1675 }
1676 }
1677
1678 let item_slot = EquipSlot::Armor(ArmorSlot::Bag1);
1680 let slot = slot_maker
1681 .fabricate(item_slot, [35.0; 2], false, false)
1682 .bottom_left_with_margins_on(
1683 state.bg_ids.bg_frame,
1684 if self.show.bag_inv { 600.0 } else { 167.0 },
1685 3.0,
1686 )
1687 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1688 .filled_slot(filled_slot);
1689
1690 let slot_id = state.ids.bag1_slot;
1691 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1692
1693 let item_slot = EquipSlot::Armor(ArmorSlot::Bag2);
1695 let slot = slot_maker
1696 .fabricate(item_slot, [35.0; 2], false, false)
1697 .down_from(state.ids.bag1_slot, 2.0)
1698 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1699 .filled_slot(filled_slot);
1700
1701 let slot_id = state.ids.bag2_slot;
1702 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1703
1704 let item_slot = EquipSlot::Armor(ArmorSlot::Bag3);
1706 let slot = slot_maker
1707 .fabricate(item_slot, [35.0; 2], false, false)
1708 .down_from(state.ids.bag2_slot, 2.0)
1709 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1710 .filled_slot(filled_slot);
1711
1712 let slot_id = state.ids.bag3_slot;
1713 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1714
1715 let item_slot = EquipSlot::Armor(ArmorSlot::Bag4);
1717 let slot = slot_maker
1718 .fabricate(item_slot, [35.0; 2], false, false)
1719 .down_from(state.ids.bag3_slot, 2.0)
1720 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1721 .filled_slot(filled_slot);
1722
1723 let slot_id = state.ids.bag4_slot;
1724 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1725
1726 if Button::image(self.imgs.close_btn)
1728 .w_h(24.0, 25.0)
1729 .hover_image(self.imgs.close_btn_hover)
1730 .press_image(self.imgs.close_btn_press)
1731 .top_right_with_margins_on(state.bg_ids.bg, 0.0, 0.0)
1732 .set(state.ids.bag_close, ui)
1733 .was_clicked()
1734 {
1735 events.push(Event::Close);
1736 }
1737 }
1738
1739 events
1740 }
1741}