1use super::{
2 CRITICAL_HP_COLOR, HudInfo, LOW_HP_COLOR, Show, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, cr_color,
3 img_ids::{Imgs, ImgsRot},
4 item_imgs::ItemImgs,
5 slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager},
6 util,
7};
8use crate::{
9 GlobalState,
10 game_input::GameInput,
11 ui::{
12 ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip, TooltipManager,
13 Tooltipable,
14 fonts::Fonts,
15 slot::{ContentSize, SlotMaker},
16 },
17};
18use client::Client;
19use common::{
20 assets::AssetExt,
21 combat::{Damage, combat_rating, perception_dist_multiplier_from_stealth},
22 comp::{
23 Body, Energy, Health, Inventory, Poise, SkillSet, Stats,
24 inventory::{InventorySortOrder, slot::Slot},
25 item::{ItemDef, ItemDesc, ItemI18n, MaterialStatManifest, Quality},
26 },
27 recipe::RecipeBookManifest,
28};
29use conrod_core::{
30 Color, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon, color,
31 widget::{self, Button, Image, Rectangle, Scrollbar, State as ConrodState, Text},
32 widget_ids,
33};
34use i18n::Localization;
35use std::borrow::Cow;
36
37use crate::hud::slots::SlotKind;
38use specs::Entity as EcsEntity;
39use std::{borrow::Borrow, sync::Arc};
40use vek::Vec2;
41
42widget_ids! {
43 pub struct InventoryScrollerIds {
44 test,
45 bag_close,
46 inv_alignment,
47 inv_grid_1,
48 inv_grid_2,
49 inv_scrollbar,
50 inv_slots_0,
51 inv_slots[],
52 inv_slot_names[],
53 inv_slot_amounts[],
54 bg,
55 bg_frame,
56 char_ico,
57 coin_ico,
58 space_txt,
59 coin_txt,
60 inventory_title,
61 inventory_title_bg,
62 scrollbar_bg,
63 second_phase_scrollbar_bg,
64 scrollbar_slots,
65 left_scrollbar_slots,
66 }
67}
68
69pub struct InventoryScrollerState {
70 ids: InventoryScrollerIds,
71}
72
73#[derive(WidgetCommon)]
74pub struct InventoryScroller<'a> {
75 client: &'a Client,
76 imgs: &'a Imgs,
77 item_imgs: &'a ItemImgs,
78 fonts: &'a Fonts,
79 #[conrod(common_builder)]
80 common: widget::CommonBuilder,
81 item_tooltip_manager: &'a mut ItemTooltipManager,
82 slot_manager: &'a mut SlotManager,
83 pulse: f32,
84 localized_strings: &'a Localization,
85 item_i18n: &'a ItemI18n,
86 show_stats: bool,
87 show_bag_inv: bool,
88 on_right: bool,
89 item_tooltip: &'a ItemTooltip<'a>,
90 playername: String,
91 entity: EcsEntity,
92 is_us: bool,
93 inventory: &'a Inventory,
94 bg_ids: &'a BackgroundIds,
95 show_salvage: bool,
96 details_mode: bool,
97}
98
99impl<'a> InventoryScroller<'a> {
100 #[expect(clippy::too_many_arguments)]
101 pub fn new(
102 client: &'a Client,
103 imgs: &'a Imgs,
104 item_imgs: &'a ItemImgs,
105 fonts: &'a Fonts,
106 item_tooltip_manager: &'a mut ItemTooltipManager,
107 slot_manager: &'a mut SlotManager,
108 pulse: f32,
109 localized_strings: &'a Localization,
110 item_i18n: &'a ItemI18n,
111 show_stats: bool,
112 show_bag_inv: bool,
113 on_right: bool,
114 item_tooltip: &'a ItemTooltip<'a>,
115 playername: String,
116 entity: EcsEntity,
117 is_us: bool,
118 inventory: &'a Inventory,
119 bg_ids: &'a BackgroundIds,
120 show_salvage: bool,
121 details_mode: bool,
122 ) -> Self {
123 InventoryScroller {
124 client,
125 imgs,
126 item_imgs,
127 fonts,
128 common: widget::CommonBuilder::default(),
129 item_tooltip_manager,
130 slot_manager,
131 pulse,
132 localized_strings,
133 item_i18n,
134 show_stats,
135 show_bag_inv,
136 on_right,
137 item_tooltip,
138 playername,
139 entity,
140 is_us,
141 inventory,
142 bg_ids,
143 show_salvage,
144 details_mode,
145 }
146 }
147
148 fn background(&mut self, ui: &mut UiCell<'_>) {
149 let bg_id = if !self.on_right {
150 self.imgs.inv_bg_bag
151 } else {
152 self.imgs.player_inv_bg_bag
153 };
154
155 let img_id = if !self.on_right {
156 self.imgs.inv_frame_bag
157 } else {
158 self.imgs.player_inv_frame_bag
159 };
160
161 let mut bg = Image::new(if self.show_stats {
162 self.imgs.inv_bg_stats
163 } else if self.show_bag_inv {
164 bg_id
165 } else {
166 self.imgs.inv_bg_armor
167 })
168 .w_h(
169 424.0,
170 if self.show_bag_inv && !self.on_right {
171 548.0
172 } else {
173 708.0
174 },
175 );
176
177 if self.on_right {
178 bg = bg.bottom_right_with_margins_on(ui.window, 70.0, 5.0);
179 } else {
180 bg = bg.bottom_left_with_margins_on(ui.window, 230.0, 5.0);
181 }
182
183 bg.color(Some(UI_MAIN)).set(self.bg_ids.bg, ui);
184
185 Image::new(if self.show_bag_inv {
186 img_id
187 } else {
188 self.imgs.inv_frame
189 })
190 .w_h(
191 424.0,
192 if self.show_bag_inv && !self.on_right {
193 548.0
194 } else {
195 708.0
196 },
197 )
198 .middle_of(self.bg_ids.bg)
199 .color(Some(UI_HIGHLIGHT_0))
200 .set(self.bg_ids.bg_frame, ui);
201 }
202
203 fn title(&mut self, state: &ConrodState<'_, InventoryScrollerState>, ui: &mut UiCell<'_>) {
204 Text::new(
205 &self
206 .localized_strings
207 .get_msg_ctx("hud-bag-inventory", &i18n::fluent_args! {
208 "playername" => &*self.playername,
209 }),
210 )
211 .mid_top_with_margin_on(self.bg_ids.bg_frame, 9.0)
212 .font_id(self.fonts.cyri.conrod_id)
213 .font_size(self.fonts.cyri.scale(22))
214 .color(Color::Rgba(0.0, 0.0, 0.0, 1.0))
215 .set(state.ids.inventory_title_bg, ui);
216 Text::new(
217 &self
218 .localized_strings
219 .get_msg_ctx("hud-bag-inventory", &i18n::fluent_args! {
220 "playername" => &*self.playername,
221 }),
222 )
223 .top_left_with_margins_on(state.ids.inventory_title_bg, 2.0, 2.0)
224 .font_id(self.fonts.cyri.conrod_id)
225 .font_size(self.fonts.cyri.scale(22))
226 .color(TEXT_COLOR)
227 .set(state.ids.inventory_title, ui);
228 }
229
230 fn scrollbar_and_slots(
231 &mut self,
232 state: &mut ConrodState<'_, InventoryScrollerState>,
233 ui: &mut UiCell<'_>,
234 ) {
235 let space_max = self.inventory.slots().count();
236
237 if space_max > 45 && !self.show_bag_inv {
239 Image::new(self.imgs.scrollbar_bg)
241 .w_h(9.0, 173.0)
242 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
243 .color(Some(UI_HIGHLIGHT_0))
244 .set(state.ids.scrollbar_bg, ui);
245 Scrollbar::y_axis(state.ids.inv_alignment)
247 .thickness(5.0)
248 .h(123.0)
249 .color(UI_MAIN)
250 .middle_of(state.ids.scrollbar_bg)
251 .set(state.ids.scrollbar_slots, ui);
252 } else if space_max > 135 && self.on_right {
253 Image::new(self.imgs.scrollbar_bg_big)
255 .w_h(9.0, 592.0)
256 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
257 .color(Some(UI_HIGHLIGHT_0))
258 .set(state.ids.scrollbar_bg, ui);
259 Scrollbar::y_axis(state.ids.inv_alignment)
261 .thickness(5.0)
262 .h(542.0)
263 .color(UI_MAIN)
264 .middle_of(state.ids.scrollbar_bg)
265 .set(state.ids.scrollbar_slots, ui);
266 };
267
268 if space_max >= 108 && !self.on_right && self.show_bag_inv {
270 Image::new(self.imgs.second_phase_scrollbar_bg)
272 .w_h(9.0, 434.0)
273 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 42.0, 3.0)
274 .color(Some(UI_HIGHLIGHT_0))
275 .set(state.ids.second_phase_scrollbar_bg, ui);
276 Scrollbar::y_axis(state.ids.inv_alignment)
278 .thickness(5.0)
279 .h(384.0)
280 .color(UI_MAIN)
281 .middle_of(state.ids.second_phase_scrollbar_bg)
282 .set(state.ids.left_scrollbar_slots, ui);
283 }
284
285 let grid_width = 362.0;
286 let grid_height = if self.show_bag_inv && !self.on_right {
287 440.0 } else if self.show_bag_inv && self.on_right {
289 600.0 } else {
291 200.0
292 };
293
294 Rectangle::fill_with([grid_width, grid_height], color::TRANSPARENT)
296 .bottom_left_with_margins_on(
297 self.bg_ids.bg_frame,
298 29.0,
299 if self.show_bag_inv && !self.on_right {
300 28.0
301 } else {
302 46.5
303 },
304 )
305 .scroll_kids_vertically()
306 .set(state.ids.inv_alignment, ui);
307
308 if state.ids.inv_slots.len() < self.inventory.capacity() {
311 state.update(|s| {
312 s.ids.inv_slots.resize(
313 self.inventory.capacity() + self.inventory.overflow_items().count(),
314 &mut ui.widget_id_generator(),
315 );
316 });
317 }
318 if state.ids.inv_slot_names.len() < self.inventory.capacity() {
319 state.update(|s| {
320 s.ids
321 .inv_slot_names
322 .resize(self.inventory.capacity(), &mut ui.widget_id_generator());
323 });
324 }
325 if state.ids.inv_slot_amounts.len() < self.inventory.capacity() {
326 state.update(|s| {
327 s.ids
328 .inv_slot_amounts
329 .resize(self.inventory.capacity(), &mut ui.widget_id_generator());
330 });
331 }
332 let mouseover_loadout_slots = self
335 .slot_manager
336 .mouse_over_slot
337 .and_then(|x| {
338 if let SlotKind::Equip(e) = x {
339 self.inventory.get_slot_range_for_equip_slot(e)
340 } else {
341 None
342 }
343 })
344 .unwrap_or(0usize..0usize);
345
346 let mut slot_maker = SlotMaker {
348 empty_slot: self.imgs.inv_slot,
349 filled_slot: self.imgs.inv_slot,
350 selected_slot: self.imgs.inv_slot_sel,
351 background_color: Some(UI_MAIN),
352 content_size: ContentSize {
353 width_height_ratio: 1.0,
354 max_fraction: 0.75,
355 },
356 selected_content_scale: 1.067,
357 amount_font: self.fonts.cyri.conrod_id,
358 amount_margins: Vec2::new(-4.0, 0.0),
359 amount_font_size: self.fonts.cyri.scale(12),
360 amount_text_color: TEXT_COLOR,
361 content_source: self.inventory,
362 image_source: self.item_imgs,
363 slot_manager: Some(self.slot_manager),
364 pulse: self.pulse,
365 };
366
367 let mut i = 0;
368 let mut items = self
369 .inventory
370 .slots_with_id()
371 .map(|(slot, item)| (Slot::Inventory(slot), item.as_ref()))
372 .chain(
373 self.inventory
374 .overflow_items()
375 .enumerate()
376 .map(|(i, item)| (Slot::Overflow(i), Some(item))),
377 )
378 .collect::<Vec<_>>();
379 if self.details_mode && !self.is_us {
380 items.sort_by_cached_key(|(_, item)| {
381 (
382 item.is_none(),
383 item.as_ref().map(|i| {
384 (
385 std::cmp::Reverse(i.quality()),
386 {
387 let (name, _) =
389 util::item_text(i, self.localized_strings, self.item_i18n);
390 name
391 },
392 i.amount(),
393 )
394 }),
395 )
396 });
397 }
398 for (pos, item) in items.into_iter() {
399 if self.details_mode && !self.is_us && item.is_none() {
400 continue;
401 }
402 let (x, y) = if self.details_mode {
403 (0, i)
404 } else {
405 (i % 9, i / 9)
406 };
407 let slot_size = if self.details_mode { 20.0 } else { 40.0 };
408
409 let mut slot_widget = slot_maker
411 .fabricate(
412 InventorySlot {
413 slot: pos,
414 ours: self.is_us,
415 entity: self.entity,
416 },
417 [slot_size as f32; 2],
418 )
419 .top_left_with_margins_on(
420 state.ids.inv_alignment,
421 0.0 + y as f64 * slot_size,
422 0.0 + x as f64 * slot_size,
423 );
424
425 if mouseover_loadout_slots.contains(&i) {
427 slot_widget = slot_widget.with_background_color(Color::Rgba(1.0, 1.0, 1.0, 1.0));
428 }
429
430 if self.show_salvage && item.as_ref().is_some_and(|item| item.is_salvageable()) {
431 slot_widget = slot_widget.with_background_color(Color::Rgba(1.0, 1.0, 1.0, 1.0));
432 }
433
434 if matches!(pos, Slot::Overflow(_)) {
436 slot_widget = slot_widget.with_background_color(Color::Rgba(1.0, 0.0, 0.0, 1.0));
437 }
438
439 if let Some(item) = item {
440 let quality_col_img = match item.quality() {
441 Quality::Low => self.imgs.inv_slot_grey,
442 Quality::Common => self.imgs.inv_slot_common,
443 Quality::Moderate => self.imgs.inv_slot_green,
444 Quality::High => self.imgs.inv_slot_blue,
445 Quality::Epic => self.imgs.inv_slot_purple,
446 Quality::Legendary => self.imgs.inv_slot_gold,
447 Quality::Artifact => self.imgs.inv_slot_orange,
448 _ => self.imgs.inv_slot_red,
449 };
450
451 let prices_info = self
452 .client
453 .pending_trade()
454 .as_ref()
455 .and_then(|(_, _, prices)| prices.clone());
456
457 if self.show_salvage && item.is_salvageable() {
458 let salvage_result: Vec<_> = item
459 .salvage_output()
460 .map(|(material_id, _)| Arc::<ItemDef>::load_expect_cloned(material_id))
461 .map(|item| item as Arc<dyn ItemDesc>)
462 .collect();
463
464 let items = salvage_result
465 .iter()
466 .map(|item| item.borrow())
467 .chain(core::iter::once(item as &dyn ItemDesc));
468
469 slot_widget
470 .filled_slot(quality_col_img)
471 .with_item_tooltip(
472 self.item_tooltip_manager,
473 items,
474 &prices_info,
475 self.item_tooltip,
476 )
477 .set(state.ids.inv_slots[i], ui);
478 } else {
479 slot_widget
480 .filled_slot(quality_col_img)
481 .with_item_tooltip(
482 self.item_tooltip_manager,
483 core::iter::once(item as &dyn ItemDesc),
484 &prices_info,
485 self.item_tooltip,
486 )
487 .set(state.ids.inv_slots[i], ui);
488 }
489 if self.details_mode {
490 let (name, _) = util::item_text(item, self.localized_strings, self.item_i18n);
491 Text::new(&name)
492 .top_left_with_margins_on(
493 state.ids.inv_alignment,
494 0.0 + y as f64 * slot_size,
495 30.0 + x as f64 * slot_size,
496 )
497 .font_id(self.fonts.cyri.conrod_id)
498 .font_size(self.fonts.cyri.scale(14))
499 .color(color::WHITE)
500 .set(state.ids.inv_slot_names[i], ui);
501
502 Text::new(&format!("{}", item.amount()))
503 .top_left_with_margins_on(
504 state.ids.inv_alignment,
505 0.0 + y as f64 * slot_size,
506 grid_width - 40.0 + x as f64 * slot_size,
507 )
508 .font_id(self.fonts.cyri.conrod_id)
509 .font_size(self.fonts.cyri.scale(14))
510 .color(color::WHITE)
511 .set(state.ids.inv_slot_amounts[i], ui);
512 }
513 } else {
514 slot_widget.set(state.ids.inv_slots[i], ui);
515 }
516 i += 1;
517 }
518 }
519
520 fn footer_metrics(
521 &mut self,
522 state: &ConrodState<'_, InventoryScrollerState>,
523 ui: &mut UiCell<'_>,
524 ) {
525 let space_used = self.inventory.populated_slots();
526 let space_max = self.inventory.slots().count();
527 let bag_space = format!("{}/{}", space_used, space_max);
528 let bag_space_percentage = space_used as f32 / space_max as f32;
529 Text::new(&bag_space)
562 .bottom_right_with_margins_on(self.bg_ids.bg_frame, 6.0, 43.0)
563 .font_id(self.fonts.cyri.conrod_id)
564 .font_size(self.fonts.cyri.scale(14))
565 .color(if bag_space_percentage < 0.8 {
566 TEXT_COLOR
567 } else if bag_space_percentage < 1.0 {
568 LOW_HP_COLOR
569 } else {
570 CRITICAL_HP_COLOR
571 })
572 .set(state.ids.space_txt, ui);
573 }
574}
575
576impl Widget for InventoryScroller<'_> {
577 type Event = ();
578 type State = InventoryScrollerState;
579 type Style = ();
580
581 fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
582 InventoryScrollerState {
583 ids: InventoryScrollerIds::new(id_gen),
584 }
585 }
586
587 fn style(&self) -> Self::Style {}
588
589 fn update(mut self, args: widget::UpdateArgs<Self>) -> Self::Event {
590 let widget::UpdateArgs { state, ui, .. } = args;
591 self.background(ui);
592 self.title(state, ui);
593 self.scrollbar_and_slots(state, ui);
594 self.footer_metrics(state, ui);
595 }
596}
597
598widget_ids! {
599 pub struct BackgroundIds {
600 bg,
601 bg_frame,
602 }
603}
604
605widget_ids! {
606 pub struct BagIds {
607 test,
608 inventory_scroller,
609 bag_close,
610 char_ico,
612 coin_ico,
613 space_txt,
614 inventory_title,
615 inventory_title_bg,
616 inventory_sort,
617 inventory_sort_selected,
618 scrollbar_bg,
619 scrollbar_slots,
620 tab_1,
621 tab_2,
622 tab_3,
623 tab_4,
624 bag_expand_btn,
625 bag_details_btn,
626 slots_bg,
628 head_slot,
629 neck_slot,
630 chest_slot,
631 shoulders_slot,
632 hands_slot,
633 legs_slot,
634 belt_slot,
635 lantern_slot,
636 ring1_slot,
637 ring2_slot,
638 feet_slot,
639 back_slot,
640 tabard_slot,
641 glider_slot,
642 active_mainhand_slot,
643 active_offhand_slot,
644 inactive_mainhand_slot,
645 inactive_offhand_slot,
646 swap_equipped_weapons_btn,
647 bag1_slot,
648 bag2_slot,
649 bag3_slot,
650 bag4_slot,
651 stat_icons[],
653 stat_txts[],
654 }
655}
656
657#[derive(WidgetCommon)]
658pub struct Bag<'a> {
659 client: &'a Client,
660 info: &'a HudInfo<'a>,
661 global_state: &'a GlobalState,
662 imgs: &'a Imgs,
663 item_imgs: &'a ItemImgs,
664 fonts: &'a Fonts,
665 #[conrod(common_builder)]
666 common: widget::CommonBuilder,
667 rot_imgs: &'a ImgsRot,
668 tooltip_manager: &'a mut TooltipManager,
669 item_tooltip_manager: &'a mut ItemTooltipManager,
670 slot_manager: &'a mut SlotManager,
671 pulse: f32,
672 localized_strings: &'a Localization,
673 item_i18n: &'a ItemI18n,
674 stats: &'a Stats,
675 skill_set: &'a SkillSet,
676 health: &'a Health,
677 energy: &'a Energy,
678 show: &'a Show,
679 body: &'a Body,
680 msm: &'a MaterialStatManifest,
681 rbm: &'a RecipeBookManifest,
682 poise: &'a Poise,
683}
684
685impl<'a> Bag<'a> {
686 #[expect(clippy::too_many_arguments)]
687 pub fn new(
688 client: &'a Client,
689 info: &'a HudInfo,
690 global_state: &'a GlobalState,
691 imgs: &'a Imgs,
692 item_imgs: &'a ItemImgs,
693 fonts: &'a Fonts,
694 rot_imgs: &'a ImgsRot,
695 tooltip_manager: &'a mut TooltipManager,
696 item_tooltip_manager: &'a mut ItemTooltipManager,
697 slot_manager: &'a mut SlotManager,
698 pulse: f32,
699 localized_strings: &'a Localization,
700 item_i18n: &'a ItemI18n,
701 stats: &'a Stats,
702 skill_set: &'a SkillSet,
703 health: &'a Health,
704 energy: &'a Energy,
705 show: &'a Show,
706 body: &'a Body,
707 msm: &'a MaterialStatManifest,
708 rbm: &'a RecipeBookManifest,
709 poise: &'a Poise,
710 ) -> Self {
711 Self {
712 client,
713 info,
714 global_state,
715 imgs,
716 item_imgs,
717 fonts,
718 common: widget::CommonBuilder::default(),
719 rot_imgs,
720 tooltip_manager,
721 item_tooltip_manager,
722 slot_manager,
723 pulse,
724 localized_strings,
725 item_i18n,
726 stats,
727 skill_set,
728 energy,
729 health,
730 show,
731 body,
732 msm,
733 rbm,
734 poise,
735 }
736 }
737}
738const STATS: [&str; 6] = [
739 "Health",
740 "Energy",
741 "Protection",
742 "Combat Rating",
743 "Stun Resilience",
744 "Stealth",
745];
746
747pub struct BagState {
748 ids: BagIds,
749 bg_ids: BackgroundIds,
750}
751
752pub enum Event {
753 BagExpand,
754 Close,
755 ChangeInventorySortOrder(InventorySortOrder),
756 SortInventory(InventorySortOrder),
757 SwapEquippedWeapons,
758 SetDetailsMode(bool),
759}
760
761impl Widget for Bag<'_> {
762 type Event = Option<Event>;
763 type State = BagState;
764 type Style = ();
765
766 fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
767 BagState {
768 bg_ids: BackgroundIds {
769 bg: id_gen.next(),
770 bg_frame: id_gen.next(),
771 },
772 ids: BagIds::new(id_gen),
773 }
774 }
775
776 fn style(&self) -> Self::Style {}
777
778 fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
779 common_base::prof_span!("Bag::update");
780 let widget::UpdateArgs { state, ui, .. } = args;
781 let i18n = &self.localized_strings;
782
783 let mut event = None;
784 let bag_tooltip = Tooltip::new({
785 let edge = &self.rot_imgs.tt_side;
788 let corner = &self.rot_imgs.tt_corner;
789 ImageFrame::new(
790 [edge.cw180, edge.none, edge.cw270, edge.cw90],
791 [corner.none, corner.cw270, corner.cw90, corner.cw180],
792 Color::Rgba(0.08, 0.07, 0.04, 1.0),
793 5.0,
794 )
795 })
796 .title_font_size(self.fonts.cyri.scale(15))
797 .parent(ui.window)
798 .desc_font_size(self.fonts.cyri.scale(12))
799 .font_id(self.fonts.cyri.conrod_id)
800 .desc_text_color(TEXT_COLOR);
801 let inventories = self.client.inventories();
802 let inventory = inventories.get(self.info.viewpoint_entity)?;
803
804 let tooltip = Tooltip::new({
806 let edge = &self.rot_imgs.tt_side;
809 let corner = &self.rot_imgs.tt_corner;
810 ImageFrame::new(
811 [edge.cw180, edge.none, edge.cw270, edge.cw90],
812 [corner.none, corner.cw270, corner.cw90, corner.cw180],
813 Color::Rgba(0.08, 0.07, 0.04, 1.0),
814 5.0,
815 )
816 })
817 .title_font_size(self.fonts.cyri.scale(15))
818 .parent(ui.window)
819 .desc_font_size(self.fonts.cyri.scale(12))
820 .font_id(self.fonts.cyri.conrod_id)
821 .desc_text_color(TEXT_COLOR);
822
823 let item_tooltip = ItemTooltip::new(
824 {
825 let edge = &self.rot_imgs.tt_side;
828 let corner = &self.rot_imgs.tt_corner;
829 ImageFrame::new(
830 [edge.cw180, edge.none, edge.cw270, edge.cw90],
831 [corner.none, corner.cw270, corner.cw90, corner.cw180],
832 Color::Rgba(0.08, 0.07, 0.04, 1.0),
833 5.0,
834 )
835 },
836 self.client,
837 self.info,
838 self.imgs,
839 self.item_imgs,
840 self.pulse,
841 self.msm,
842 self.rbm,
843 Some(inventory),
844 self.localized_strings,
845 self.item_i18n,
846 )
847 .title_font_size(self.fonts.cyri.scale(20))
848 .parent(ui.window)
849 .desc_font_size(self.fonts.cyri.scale(12))
850 .font_id(self.fonts.cyri.conrod_id)
851 .desc_text_color(TEXT_COLOR);
852
853 InventoryScroller::new(
854 self.client,
855 self.imgs,
856 self.item_imgs,
857 self.fonts,
858 self.item_tooltip_manager,
859 self.slot_manager,
860 self.pulse,
861 self.localized_strings,
862 self.item_i18n,
863 self.show.stats,
864 self.show.bag_inv,
865 true,
866 &item_tooltip,
867 self.localized_strings.get_content(&self.stats.name),
868 self.info.viewpoint_entity,
869 true,
870 inventory,
871 &state.bg_ids,
872 self.show.crafting_fields.salvage,
873 self.show.bag_details,
874 )
875 .set(state.ids.inventory_scroller, ui);
876
877 Image::new(self.imgs.char_art)
879 .w_h(40.0, 37.0)
880 .top_left_with_margins_on(state.bg_ids.bg, 4.0, 2.0)
881 .set(state.ids.char_ico, ui);
882
883 let buttons_top = if self.show.bag_inv { 53.0 } else { 460.0 };
884 let (txt, btn, hover, press) = if self.show.bag_details {
885 (
886 "Grid mode",
887 self.imgs.grid_btn,
888 self.imgs.grid_btn_hover,
889 self.imgs.grid_btn_press,
890 )
891 } else {
892 (
893 "List mode",
894 self.imgs.list_btn,
895 self.imgs.list_btn_hover,
896 self.imgs.list_btn_press,
897 )
898 };
899 let details_btn = Button::image(btn)
900 .w_h(32.0, 17.0)
901 .hover_image(hover)
902 .press_image(press);
903 if details_btn
904 .mid_top_with_margin_on(state.bg_ids.bg_frame, buttons_top)
905 .with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
906 .set(state.ids.bag_details_btn, ui)
907 .was_clicked()
908 {
909 event = Some(Event::SetDetailsMode(!self.show.bag_details));
910 }
911 let (txt, btn, hover, press) = if self.show.bag_inv {
913 (
914 "Show Loadout",
915 self.imgs.collapse_btn,
916 self.imgs.collapse_btn_hover,
917 self.imgs.collapse_btn_press,
918 )
919 } else {
920 (
921 "Expand Bag",
922 self.imgs.expand_btn,
923 self.imgs.expand_btn_hover,
924 self.imgs.expand_btn_press,
925 )
926 };
927 let expand_btn = Button::image(btn)
928 .w_h(30.0, 17.0)
929 .hover_image(hover)
930 .press_image(press);
931
932 if (inventory.slots().count() > 45 || self.show.bag_inv)
934 && expand_btn
935 .top_right_with_margins_on(state.bg_ids.bg_frame, buttons_top, 37.0)
936 .with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
937 .set(state.ids.bag_expand_btn, ui)
938 .was_clicked()
939 {
940 event = Some(Event::BagExpand);
941 }
942
943 if Button::image(self.imgs.inv_sort_btn)
945 .w_h(30.0, 17.0)
946 .hover_image(self.imgs.inv_sort_btn_hover)
947 .press_image(self.imgs.inv_sort_btn_press)
948 .top_left_with_margins_on(state.bg_ids.bg_frame, buttons_top, 87.0) .with_tooltip(
950 self.tooltip_manager,
951 &(match self.global_state.settings.inventory.sort_order.next() {
952 InventorySortOrder::Name => i18n.get_msg("hud-bag-change_to_sort_by_name"),
953 InventorySortOrder::Quality => i18n.get_msg("hud-bag-change_to_sort_by_quality"),
954 InventorySortOrder::Category => i18n.get_msg("hud-bag-change_to_sort_by_category"),
955 InventorySortOrder::Tag => i18n.get_msg("hud-bag-change_to_sort_by_tag"),
956 InventorySortOrder::Amount => i18n.get_msg("hud-bag-change_to_sort_by_quantity"),
957 }),
958 "",
959 &tooltip,
960 color::WHITE,
961 )
962 .set(state.ids.inventory_sort, ui)
963 .was_clicked()
964 {
965 event = Some(Event::ChangeInventorySortOrder(
967 self.global_state.settings.inventory.sort_order.next(),
968 ));
969 }
970 if Button::image(self.imgs.inv_sort_selected_btn)
972 .w_h(30.0, 17.0)
973 .hover_image(self.imgs.inv_sort_selected_btn_hover)
974 .press_image(self.imgs.inv_sort_selected_btn_press)
975 .top_left_with_margins_on(state.bg_ids.bg_frame, buttons_top, 47.0)
976 .with_tooltip(
977 self.tooltip_manager,
978 &(match self.global_state.settings.inventory.sort_order {
979 InventorySortOrder::Name => i18n.get_msg("hud-bag-sort_by_name"),
980 InventorySortOrder::Quality => i18n.get_msg("hud-bag-sort_by_quality"),
981 InventorySortOrder::Category => i18n.get_msg("hud-bag-sort_by_category"),
982 InventorySortOrder::Tag => i18n.get_msg("hud-bag-sort_by_tag"),
983 InventorySortOrder::Amount => i18n.get_msg("hud-bag-sort_by_quantity"),
984 }),
985 "",
986 &tooltip,
987 color::WHITE,
988 )
989 .set(state.ids.inventory_sort_selected, ui)
990 .was_clicked()
991 {
992 event = Some(Event::SortInventory(
993 self.global_state.settings.inventory.sort_order,
994 ));
995 }
996
997 let mut slot_maker = SlotMaker {
999 empty_slot: self.imgs.armor_slot_empty,
1000 filled_slot: self.imgs.armor_slot,
1001 selected_slot: self.imgs.armor_slot_sel,
1002 background_color: Some(UI_HIGHLIGHT_0),
1003 content_size: ContentSize {
1004 width_height_ratio: 1.0,
1005 max_fraction: 0.75, },
1009 selected_content_scale: 1.067,
1010 amount_font: self.fonts.cyri.conrod_id,
1011 amount_margins: Vec2::new(-4.0, 0.0),
1012 amount_font_size: self.fonts.cyri.scale(12),
1013 amount_text_color: TEXT_COLOR,
1014 content_source: inventory,
1015 image_source: self.item_imgs,
1016 slot_manager: Some(self.slot_manager),
1017 pulse: self.pulse,
1018 };
1019
1020 macro_rules! set_tooltip {
1041 ($slot_maker:expr, $slot_id:expr, $slot:expr, $desc:expr) => {
1042 if let Some(item) = inventory.equipped($slot) {
1043 let manager = &mut *self.item_tooltip_manager;
1044 $slot_maker
1045 .with_item_tooltip(
1046 manager,
1047 core::iter::once(item as &dyn ItemDesc),
1048 &None,
1049 &item_tooltip,
1050 )
1051 .set($slot_id, ui)
1052 } else {
1053 let manager = &mut *self.tooltip_manager;
1054 $slot_maker
1055 .with_tooltip(manager, &i18n.get_msg($desc), "", &tooltip, color::WHITE)
1056 .set($slot_id, ui)
1057 }
1058 };
1059 }
1060
1061 let filled_slot = self.imgs.armor_slot;
1062 if !self.show.bag_inv {
1063 state.update(|s| {
1065 s.ids
1066 .stat_icons
1067 .resize(STATS.len(), &mut ui.widget_id_generator())
1068 });
1069 state.update(|s| {
1070 s.ids
1071 .stat_txts
1072 .resize(STATS.len(), &mut ui.widget_id_generator())
1073 });
1074 let combat_rating = combat_rating(
1076 inventory,
1077 self.health,
1078 self.energy,
1079 self.poise,
1080 self.skill_set,
1081 *self.body,
1082 self.msm,
1083 )
1084 .min(999.9);
1085 let indicator_col = cr_color(combat_rating);
1086 for i in STATS.iter().copied().enumerate() {
1087 let btn = Button::image(match i.1 {
1088 "Health" => self.imgs.health_ico,
1089 "Energy" => self.imgs.energy_ico,
1090 "Combat Rating" => self.imgs.combat_rating_ico,
1091 "Protection" => self.imgs.protection_ico,
1092 "Stun Resilience" => self.imgs.stun_res_ico,
1093 "Stealth" => self.imgs.stealth_rating_ico,
1094 _ => self.imgs.nothing,
1095 })
1096 .w_h(20.0, 20.0)
1097 .image_color(if i.1 == "Combat Rating" {
1098 indicator_col
1099 } else {
1100 TEXT_COLOR
1101 });
1102 let protection_txt = format!(
1103 "{}%",
1104 (100.0
1105 * Damage::compute_damage_reduction(
1106 None,
1107 Some(inventory),
1108 Some(self.stats),
1109 self.msm
1110 )) as i32
1111 );
1112 let health_txt = format!("{}", self.health.maximum().round() as usize);
1113 let energy_txt = format!("{}", self.energy.maximum().round() as usize);
1114 let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
1115 let stun_res_txt = format!(
1116 "{}",
1117 (100.0
1118 * Poise::compute_poise_damage_reduction(
1119 Some(inventory),
1120 self.msm,
1121 None,
1122 Some(self.stats),
1123 )) as i32
1124 );
1125 let stealth_txt = format!(
1126 "{:.1}%",
1127 ((1.0
1128 - perception_dist_multiplier_from_stealth(
1129 Some(inventory),
1130 None,
1131 self.msm
1132 ))
1133 * 100.0)
1134 );
1135 let btn = if i.0 == 0 {
1136 btn.top_left_with_margins_on(state.bg_ids.bg_frame, 55.0, 10.0)
1137 } else {
1138 btn.down_from(state.ids.stat_icons[i.0 - 1], 7.0)
1139 };
1140 let tooltip_head = match i.1 {
1141 "Health" => i18n.get_msg("hud-bag-health"),
1142 "Energy" => i18n.get_msg("hud-bag-energy"),
1143 "Combat Rating" => i18n.get_msg("hud-bag-combat_rating"),
1144 "Protection" => i18n.get_msg("hud-bag-protection"),
1145 "Stun Resilience" => i18n.get_msg("hud-bag-stun_res"),
1146 "Stealth" => i18n.get_msg("hud-bag-stealth"),
1147 _ => Cow::Borrowed(""),
1148 };
1149 let tooltip_txt = match i.1 {
1150 "Combat Rating" => i18n.get_msg("hud-bag-combat_rating_desc"),
1151 "Protection" => i18n.get_msg("hud-bag-protection_desc"),
1152 "Stun Resilience" => i18n.get_msg("hud-bag-stun_res_desc"),
1153 _ => Cow::Borrowed(""),
1154 };
1155 btn.with_tooltip(
1156 self.tooltip_manager,
1157 &tooltip_head,
1158 &tooltip_txt,
1159 &bag_tooltip,
1160 TEXT_COLOR,
1161 )
1162 .set(state.ids.stat_icons[i.0], ui);
1163 Text::new(match i.1 {
1164 "Health" => &health_txt,
1165 "Energy" => &energy_txt,
1166 "Combat Rating" => &combat_rating_txt,
1167 "Protection" => &protection_txt,
1168 "Stun Resilience" => &stun_res_txt,
1169 "Stealth" => &stealth_txt,
1170 _ => "",
1171 })
1172 .right_from(state.ids.stat_icons[i.0], 10.0)
1173 .font_id(self.fonts.cyri.conrod_id)
1174 .font_size(self.fonts.cyri.scale(14))
1175 .color(TEXT_COLOR)
1176 .graphics_for(state.ids.stat_icons[i.0])
1177 .set(state.ids.stat_txts[i.0], ui);
1178 }
1179 let item_slot = EquipSlot::Armor(ArmorSlot::Head);
1182 let slot = slot_maker
1183 .fabricate(item_slot, [45.0; 2])
1184 .mid_top_with_margin_on(state.bg_ids.bg_frame, 60.0)
1185 .with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
1186 .filled_slot(filled_slot);
1187
1188 let slot_id = state.ids.head_slot;
1189 set_tooltip!(slot, slot_id, item_slot, "hud-bag-head");
1190
1191 let item_slot = EquipSlot::Armor(ArmorSlot::Neck);
1193 let slot = slot_maker
1194 .fabricate(item_slot, [45.0; 2])
1195 .mid_bottom_with_margin_on(state.ids.head_slot, -55.0)
1196 .with_icon(self.imgs.necklace_bg, Vec2::new(40.0, 31.0), Some(UI_MAIN))
1197 .filled_slot(filled_slot);
1198
1199 let slot_id = state.ids.neck_slot;
1200 set_tooltip!(slot, slot_id, item_slot, "hud-bag-neck");
1201
1202 let item_slot = EquipSlot::Armor(ArmorSlot::Chest);
1205 let slot = slot_maker
1206 .fabricate(item_slot, [85.0; 2])
1207 .mid_bottom_with_margin_on(state.ids.neck_slot, -95.0)
1208 .with_icon(self.imgs.chest_bg, Vec2::new(64.0, 42.0), Some(UI_MAIN))
1209 .filled_slot(filled_slot);
1210
1211 let slot_id = state.ids.chest_slot;
1212 set_tooltip!(slot, slot_id, item_slot, "hud-bag-chest");
1213
1214 let item_slot = EquipSlot::Armor(ArmorSlot::Shoulders);
1216 let slot = slot_maker
1217 .fabricate(item_slot, [70.0; 2])
1218 .bottom_left_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
1219 .with_icon(self.imgs.shoulders_bg, Vec2::new(60.0, 36.0), Some(UI_MAIN))
1220 .filled_slot(filled_slot);
1221
1222 let slot_id = state.ids.shoulders_slot;
1223 set_tooltip!(slot, slot_id, item_slot, "hud-bag-shoulders");
1224
1225 let item_slot = EquipSlot::Armor(ArmorSlot::Hands);
1227 let slot = slot_maker
1228 .fabricate(item_slot, [70.0; 2])
1229 .bottom_right_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
1230 .with_icon(self.imgs.hands_bg, Vec2::new(55.0, 60.0), Some(UI_MAIN))
1231 .filled_slot(filled_slot);
1232
1233 let slot_id = state.ids.hands_slot;
1234 set_tooltip!(slot, slot_id, item_slot, "hud-bag-hands");
1235
1236 let item_slot = EquipSlot::Armor(ArmorSlot::Belt);
1238 let slot = slot_maker
1239 .fabricate(item_slot, [45.0; 2])
1240 .mid_bottom_with_margin_on(state.ids.chest_slot, -55.0)
1241 .with_icon(self.imgs.belt_bg, Vec2::new(40.0, 23.0), Some(UI_MAIN))
1242 .filled_slot(filled_slot);
1243
1244 let slot_id = state.ids.belt_slot;
1245 set_tooltip!(slot, slot_id, item_slot, "hud-bag-belt");
1246
1247 let item_slot = EquipSlot::Armor(ArmorSlot::Legs);
1249 let slot = slot_maker
1250 .fabricate(item_slot, [85.0; 2])
1251 .mid_bottom_with_margin_on(state.ids.belt_slot, -95.0)
1252 .with_icon(self.imgs.legs_bg, Vec2::new(48.0, 70.0), Some(UI_MAIN))
1253 .filled_slot(filled_slot);
1254
1255 let slot_id = state.ids.legs_slot;
1256 set_tooltip!(slot, slot_id, item_slot, "hud-bag-legs");
1257
1258 let item_slot = EquipSlot::Armor(ArmorSlot::Ring1);
1260 let slot = slot_maker
1261 .fabricate(item_slot, [45.0; 2])
1262 .bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0)
1263 .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
1264 .filled_slot(filled_slot);
1265
1266 let slot_id = state.ids.ring1_slot;
1267 set_tooltip!(slot, slot_id, item_slot, "hud-bag-ring");
1268
1269 let item_slot = EquipSlot::Armor(ArmorSlot::Ring2);
1271 let slot = slot_maker
1272 .fabricate(item_slot, [45.0; 2])
1273 .bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0)
1274 .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
1275 .filled_slot(filled_slot);
1276
1277 let slot_id = state.ids.ring2_slot;
1278 set_tooltip!(slot, slot_id, item_slot, "hud-bag-ring");
1279
1280 let item_slot = EquipSlot::Armor(ArmorSlot::Back);
1282 let slot = slot_maker
1283 .fabricate(item_slot, [45.0; 2])
1284 .down_from(state.ids.ring2_slot, 10.0)
1285 .with_icon(self.imgs.back_bg, Vec2::new(33.0, 40.0), Some(UI_MAIN))
1286 .filled_slot(filled_slot);
1287
1288 let slot_id = state.ids.back_slot;
1289 set_tooltip!(slot, slot_id, item_slot, "hud-bag-back");
1290
1291 let item_slot = EquipSlot::Armor(ArmorSlot::Feet);
1293 let slot = slot_maker
1294 .fabricate(item_slot, [45.0; 2])
1295 .down_from(state.ids.ring1_slot, 10.0)
1296 .with_icon(self.imgs.feet_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
1297 .filled_slot(filled_slot);
1298
1299 let slot_id = state.ids.feet_slot;
1300 set_tooltip!(slot, slot_id, item_slot, "hud-bag-feet");
1301
1302 let item_slot = EquipSlot::Lantern;
1304 let slot = slot_maker
1305 .fabricate(item_slot, [45.0; 2])
1306 .top_right_with_margins_on(state.bg_ids.bg_frame, 60.0, 5.0)
1307 .with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN))
1308 .filled_slot(filled_slot);
1309
1310 let slot_id = state.ids.lantern_slot;
1311 set_tooltip!(slot, slot_id, item_slot, "hud-bag-lantern");
1312
1313 let item_slot = EquipSlot::Glider;
1315 let slot = slot_maker
1316 .fabricate(item_slot, [45.0; 2])
1317 .down_from(state.ids.lantern_slot, 5.0)
1318 .with_icon(self.imgs.glider_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN))
1319 .filled_slot(filled_slot);
1320
1321 let slot_id = state.ids.glider_slot;
1322 set_tooltip!(slot, slot_id, item_slot, "hud-bag-glider");
1323
1324 let item_slot = EquipSlot::Armor(ArmorSlot::Tabard);
1326 let slot = slot_maker
1327 .fabricate(item_slot, [45.0; 2])
1328 .down_from(state.ids.glider_slot, 5.0)
1329 .with_icon(self.imgs.tabard_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN))
1330 .filled_slot(filled_slot);
1331
1332 let slot_id = state.ids.tabard_slot;
1333 set_tooltip!(slot, slot_id, item_slot, "hud-bag-tabard");
1334
1335 let item_slot = EquipSlot::ActiveMainhand;
1337 let slot = slot_maker
1338 .fabricate(item_slot, [85.0; 2])
1339 .bottom_right_with_margins_on(state.ids.back_slot, -95.0, 0.0)
1340 .with_icon(self.imgs.mainhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
1341 .filled_slot(filled_slot);
1342
1343 let slot_id = state.ids.active_mainhand_slot;
1344 set_tooltip!(slot, slot_id, item_slot, "hud-bag-mainhand");
1345
1346 let item_slot = EquipSlot::ActiveOffhand;
1348 let slot = slot_maker
1349 .fabricate(item_slot, [85.0; 2])
1350 .bottom_left_with_margins_on(state.ids.feet_slot, -95.0, 0.0)
1351 .with_icon(self.imgs.offhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
1352 .filled_slot(filled_slot);
1353
1354 let slot_id = state.ids.active_offhand_slot;
1355 set_tooltip!(slot, slot_id, item_slot, "hud-bag-offhand");
1356
1357 let item_slot = EquipSlot::InactiveMainhand;
1359 let slot = slot_maker
1360 .fabricate(item_slot, [40.0; 2])
1361 .bottom_right_with_margins_on(state.ids.active_mainhand_slot, 3.0, -47.0)
1362 .with_icon(self.imgs.mainhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
1363 .filled_slot(filled_slot);
1364
1365 let slot_id = state.ids.inactive_mainhand_slot;
1366 set_tooltip!(slot, slot_id, item_slot, "hud-bag-inactive_mainhand");
1367
1368 let item_slot = EquipSlot::InactiveOffhand;
1370 let slot = slot_maker
1371 .fabricate(item_slot, [40.0; 2])
1372 .bottom_left_with_margins_on(state.ids.active_offhand_slot, 3.0, -47.0)
1373 .with_icon(self.imgs.offhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
1374 .filled_slot(filled_slot);
1375
1376 let slot_id = state.ids.inactive_offhand_slot;
1377 set_tooltip!(slot, slot_id, item_slot, "hud-bag-inactive_offhand");
1378
1379 if Button::image(self.imgs.swap_equipped_weapons_btn)
1380 .hover_image(self.imgs.swap_equipped_weapons_btn_hover)
1381 .press_image(self.imgs.swap_equipped_weapons_btn_press)
1382 .w_h(32.0, 40.0)
1383 .bottom_left_with_margins_on(state.bg_ids.bg_frame, 0.0, 23.3)
1384 .align_middle_y_of(state.ids.active_mainhand_slot)
1385 .with_tooltip(
1386 self.tooltip_manager,
1387 &i18n.get_msg("hud-bag-swap_equipped_weapons_title"),
1388 &(if let Some(key) = self
1389 .global_state
1390 .settings
1391 .controls
1392 .get_binding(GameInput::SwapLoadout)
1393 {
1394 i18n.get_msg_ctx(
1395 "hud-bag-swap_equipped_weapons_desc",
1396 &i18n::fluent_args! {
1397 "key" => key.display_string()
1398 },
1399 )
1400 } else {
1401 Cow::Borrowed("")
1402 }),
1403 &tooltip,
1404 color::WHITE,
1405 )
1406 .set(state.ids.swap_equipped_weapons_btn, ui)
1407 .was_clicked()
1408 {
1409 event = Some(Event::SwapEquippedWeapons);
1410 }
1411 }
1412
1413 let item_slot = EquipSlot::Armor(ArmorSlot::Bag1);
1415 let slot = slot_maker
1416 .fabricate(item_slot, [35.0; 2])
1417 .bottom_left_with_margins_on(
1418 state.bg_ids.bg_frame,
1419 if self.show.bag_inv { 600.0 } else { 167.0 },
1420 3.0,
1421 )
1422 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1423 .filled_slot(filled_slot);
1424
1425 let slot_id = state.ids.bag1_slot;
1426 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1427
1428 let item_slot = EquipSlot::Armor(ArmorSlot::Bag2);
1430 let slot = slot_maker
1431 .fabricate(item_slot, [35.0; 2])
1432 .down_from(state.ids.bag1_slot, 2.0)
1433 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1434 .filled_slot(filled_slot);
1435
1436 let slot_id = state.ids.bag2_slot;
1437 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1438
1439 let item_slot = EquipSlot::Armor(ArmorSlot::Bag3);
1441 let slot = slot_maker
1442 .fabricate(item_slot, [35.0; 2])
1443 .down_from(state.ids.bag2_slot, 2.0)
1444 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1445 .filled_slot(filled_slot);
1446
1447 let slot_id = state.ids.bag3_slot;
1448 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1449
1450 let item_slot = EquipSlot::Armor(ArmorSlot::Bag4);
1452 let slot = slot_maker
1453 .fabricate(item_slot, [35.0; 2])
1454 .down_from(state.ids.bag3_slot, 2.0)
1455 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1456 .filled_slot(filled_slot);
1457
1458 let slot_id = state.ids.bag4_slot;
1459 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1460
1461 if Button::image(self.imgs.close_btn)
1463 .w_h(24.0, 25.0)
1464 .hover_image(self.imgs.close_btn_hover)
1465 .press_image(self.imgs.close_btn_press)
1466 .top_right_with_margins_on(state.bg_ids.bg, 0.0, 0.0)
1467 .set(state.ids.bag_close, ui)
1468 .was_clicked()
1469 {
1470 event = Some(Event::Close);
1471 }
1472 event
1473 }
1474}