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 scrollbar_bg,
618 scrollbar_slots,
619 tab_1,
620 tab_2,
621 tab_3,
622 tab_4,
623 bag_expand_btn,
624 bag_details_btn,
625 slots_bg,
627 head_slot,
628 neck_slot,
629 chest_slot,
630 shoulders_slot,
631 hands_slot,
632 legs_slot,
633 belt_slot,
634 lantern_slot,
635 ring1_slot,
636 ring2_slot,
637 feet_slot,
638 back_slot,
639 tabard_slot,
640 glider_slot,
641 active_mainhand_slot,
642 active_offhand_slot,
643 inactive_mainhand_slot,
644 inactive_offhand_slot,
645 swap_equipped_weapons_btn,
646 bag1_slot,
647 bag2_slot,
648 bag3_slot,
649 bag4_slot,
650 stat_icons[],
652 stat_txts[],
653 }
654}
655
656#[derive(WidgetCommon)]
657pub struct Bag<'a> {
658 client: &'a Client,
659 info: &'a HudInfo,
660 global_state: &'a GlobalState,
661 imgs: &'a Imgs,
662 item_imgs: &'a ItemImgs,
663 fonts: &'a Fonts,
664 #[conrod(common_builder)]
665 common: widget::CommonBuilder,
666 rot_imgs: &'a ImgsRot,
667 tooltip_manager: &'a mut TooltipManager,
668 item_tooltip_manager: &'a mut ItemTooltipManager,
669 slot_manager: &'a mut SlotManager,
670 pulse: f32,
671 localized_strings: &'a Localization,
672 item_i18n: &'a ItemI18n,
673 stats: &'a Stats,
674 skill_set: &'a SkillSet,
675 health: &'a Health,
676 energy: &'a Energy,
677 show: &'a Show,
678 body: &'a Body,
679 msm: &'a MaterialStatManifest,
680 rbm: &'a RecipeBookManifest,
681 poise: &'a Poise,
682}
683
684impl<'a> Bag<'a> {
685 #[expect(clippy::too_many_arguments)]
686 pub fn new(
687 client: &'a Client,
688 info: &'a HudInfo,
689 global_state: &'a GlobalState,
690 imgs: &'a Imgs,
691 item_imgs: &'a ItemImgs,
692 fonts: &'a Fonts,
693 rot_imgs: &'a ImgsRot,
694 tooltip_manager: &'a mut TooltipManager,
695 item_tooltip_manager: &'a mut ItemTooltipManager,
696 slot_manager: &'a mut SlotManager,
697 pulse: f32,
698 localized_strings: &'a Localization,
699 item_i18n: &'a ItemI18n,
700 stats: &'a Stats,
701 skill_set: &'a SkillSet,
702 health: &'a Health,
703 energy: &'a Energy,
704 show: &'a Show,
705 body: &'a Body,
706 msm: &'a MaterialStatManifest,
707 rbm: &'a RecipeBookManifest,
708 poise: &'a Poise,
709 ) -> Self {
710 Self {
711 client,
712 info,
713 global_state,
714 imgs,
715 item_imgs,
716 fonts,
717 common: widget::CommonBuilder::default(),
718 rot_imgs,
719 tooltip_manager,
720 item_tooltip_manager,
721 slot_manager,
722 pulse,
723 localized_strings,
724 item_i18n,
725 stats,
726 skill_set,
727 energy,
728 health,
729 show,
730 body,
731 msm,
732 rbm,
733 poise,
734 }
735 }
736}
737const STATS: [&str; 6] = [
738 "Health",
739 "Energy",
740 "Protection",
741 "Combat Rating",
742 "Stun Resilience",
743 "Stealth",
744];
745
746pub struct BagState {
747 ids: BagIds,
748 bg_ids: BackgroundIds,
749}
750
751pub enum Event {
752 BagExpand,
753 Close,
754 SortInventory,
755 SwapEquippedWeapons,
756 SetDetailsMode(bool),
757}
758
759impl Widget for Bag<'_> {
760 type Event = Option<Event>;
761 type State = BagState;
762 type Style = ();
763
764 fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
765 BagState {
766 bg_ids: BackgroundIds {
767 bg: id_gen.next(),
768 bg_frame: id_gen.next(),
769 },
770 ids: BagIds::new(id_gen),
771 }
772 }
773
774 fn style(&self) -> Self::Style {}
775
776 fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
777 common_base::prof_span!("Bag::update");
778 let widget::UpdateArgs { state, ui, .. } = args;
779 let i18n = &self.localized_strings;
780 let key_layout = &self.global_state.window.key_layout;
781
782 let mut event = None;
783 let bag_tooltip = Tooltip::new({
784 let edge = &self.rot_imgs.tt_side;
787 let corner = &self.rot_imgs.tt_corner;
788 ImageFrame::new(
789 [edge.cw180, edge.none, edge.cw270, edge.cw90],
790 [corner.none, corner.cw270, corner.cw90, corner.cw180],
791 Color::Rgba(0.08, 0.07, 0.04, 1.0),
792 5.0,
793 )
794 })
795 .title_font_size(self.fonts.cyri.scale(15))
796 .parent(ui.window)
797 .desc_font_size(self.fonts.cyri.scale(12))
798 .font_id(self.fonts.cyri.conrod_id)
799 .desc_text_color(TEXT_COLOR);
800 let inventories = self.client.inventories();
801 let inventory = inventories.get(self.info.viewpoint_entity)?;
802
803 let tooltip = Tooltip::new({
805 let edge = &self.rot_imgs.tt_side;
808 let corner = &self.rot_imgs.tt_corner;
809 ImageFrame::new(
810 [edge.cw180, edge.none, edge.cw270, edge.cw90],
811 [corner.none, corner.cw270, corner.cw90, corner.cw180],
812 Color::Rgba(0.08, 0.07, 0.04, 1.0),
813 5.0,
814 )
815 })
816 .title_font_size(self.fonts.cyri.scale(15))
817 .parent(ui.window)
818 .desc_font_size(self.fonts.cyri.scale(12))
819 .font_id(self.fonts.cyri.conrod_id)
820 .desc_text_color(TEXT_COLOR);
821
822 let item_tooltip = ItemTooltip::new(
823 {
824 let edge = &self.rot_imgs.tt_side;
827 let corner = &self.rot_imgs.tt_corner;
828 ImageFrame::new(
829 [edge.cw180, edge.none, edge.cw270, edge.cw90],
830 [corner.none, corner.cw270, corner.cw90, corner.cw180],
831 Color::Rgba(0.08, 0.07, 0.04, 1.0),
832 5.0,
833 )
834 },
835 self.client,
836 self.info,
837 self.imgs,
838 self.item_imgs,
839 self.pulse,
840 self.msm,
841 self.rbm,
842 Some(inventory),
843 self.localized_strings,
844 self.item_i18n,
845 )
846 .title_font_size(self.fonts.cyri.scale(20))
847 .parent(ui.window)
848 .desc_font_size(self.fonts.cyri.scale(12))
849 .font_id(self.fonts.cyri.conrod_id)
850 .desc_text_color(TEXT_COLOR);
851
852 InventoryScroller::new(
853 self.client,
854 self.imgs,
855 self.item_imgs,
856 self.fonts,
857 self.item_tooltip_manager,
858 self.slot_manager,
859 self.pulse,
860 self.localized_strings,
861 self.item_i18n,
862 self.show.stats,
863 self.show.bag_inv,
864 true,
865 &item_tooltip,
866 self.stats.name.to_string(),
867 self.info.viewpoint_entity,
868 true,
869 inventory,
870 &state.bg_ids,
871 self.show.crafting_fields.salvage,
872 self.show.bag_details,
873 )
874 .set(state.ids.inventory_scroller, ui);
875
876 Image::new(self.imgs.char_art)
878 .w_h(40.0, 37.0)
879 .top_left_with_margins_on(state.bg_ids.bg, 4.0, 2.0)
880 .set(state.ids.char_ico, ui);
881
882 let buttons_top = if self.show.bag_inv { 53.0 } else { 460.0 };
883 let (txt, btn, hover, press) = if self.show.bag_details {
884 (
885 "Grid mode",
886 self.imgs.grid_btn,
887 self.imgs.grid_btn_hover,
888 self.imgs.grid_btn_press,
889 )
890 } else {
891 (
892 "List mode",
893 self.imgs.list_btn,
894 self.imgs.list_btn_hover,
895 self.imgs.list_btn_press,
896 )
897 };
898 let details_btn = Button::image(btn)
899 .w_h(32.0, 17.0)
900 .hover_image(hover)
901 .press_image(press);
902 if details_btn
903 .mid_top_with_margin_on(state.bg_ids.bg_frame, buttons_top)
904 .with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
905 .set(state.ids.bag_details_btn, ui)
906 .was_clicked()
907 {
908 event = Some(Event::SetDetailsMode(!self.show.bag_details));
909 }
910 let (txt, btn, hover, press) = if self.show.bag_inv {
912 (
913 "Show Loadout",
914 self.imgs.collapse_btn,
915 self.imgs.collapse_btn_hover,
916 self.imgs.collapse_btn_press,
917 )
918 } else {
919 (
920 "Expand Bag",
921 self.imgs.expand_btn,
922 self.imgs.expand_btn_hover,
923 self.imgs.expand_btn_press,
924 )
925 };
926 let expand_btn = Button::image(btn)
927 .w_h(30.0, 17.0)
928 .hover_image(hover)
929 .press_image(press);
930
931 if (inventory.slots().count() > 45 || self.show.bag_inv)
933 && expand_btn
934 .top_right_with_margins_on(state.bg_ids.bg_frame, buttons_top, 37.0)
935 .with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
936 .set(state.ids.bag_expand_btn, ui)
937 .was_clicked()
938 {
939 event = Some(Event::BagExpand);
940 }
941
942 if Button::image(self.imgs.inv_sort_btn)
944 .w_h(30.0, 17.0)
945 .hover_image(self.imgs.inv_sort_btn_hover)
946 .press_image(self.imgs.inv_sort_btn_press)
947 .top_left_with_margins_on(state.bg_ids.bg_frame, buttons_top, 47.0)
948 .with_tooltip(
949 self.tooltip_manager,
950 &(match inventory.next_sort_order() {
951 InventorySortOrder::Name => i18n.get_msg("hud-bag-sort_by_name"),
952 InventorySortOrder::Quality => i18n.get_msg("hud-bag-sort_by_quality"),
953 InventorySortOrder::Category => i18n.get_msg("hud-bag-sort_by_category"),
954 InventorySortOrder::Tag => i18n.get_msg("hud-bag-sort_by_tag"),
955 InventorySortOrder::Amount => i18n.get_msg("hud-bag-sort_by_quantity"),
956 }),
957 "",
958 &tooltip,
959 color::WHITE,
960 )
961 .set(state.ids.inventory_sort, ui)
962 .was_clicked()
963 {
964 event = Some(Event::SortInventory);
965 }
966
967 let mut slot_maker = SlotMaker {
969 empty_slot: self.imgs.armor_slot_empty,
970 filled_slot: self.imgs.armor_slot,
971 selected_slot: self.imgs.armor_slot_sel,
972 background_color: Some(UI_HIGHLIGHT_0),
973 content_size: ContentSize {
974 width_height_ratio: 1.0,
975 max_fraction: 0.75, },
979 selected_content_scale: 1.067,
980 amount_font: self.fonts.cyri.conrod_id,
981 amount_margins: Vec2::new(-4.0, 0.0),
982 amount_font_size: self.fonts.cyri.scale(12),
983 amount_text_color: TEXT_COLOR,
984 content_source: inventory,
985 image_source: self.item_imgs,
986 slot_manager: Some(self.slot_manager),
987 pulse: self.pulse,
988 };
989
990 macro_rules! set_tooltip {
1011 ($slot_maker:expr, $slot_id:expr, $slot:expr, $desc:expr) => {
1012 if let Some(item) = inventory.equipped($slot) {
1013 let manager = &mut *self.item_tooltip_manager;
1014 $slot_maker
1015 .with_item_tooltip(
1016 manager,
1017 core::iter::once(item as &dyn ItemDesc),
1018 &None,
1019 &item_tooltip,
1020 )
1021 .set($slot_id, ui)
1022 } else {
1023 let manager = &mut *self.tooltip_manager;
1024 $slot_maker
1025 .with_tooltip(manager, &i18n.get_msg($desc), "", &tooltip, color::WHITE)
1026 .set($slot_id, ui)
1027 }
1028 };
1029 }
1030
1031 let filled_slot = self.imgs.armor_slot;
1032 if !self.show.bag_inv {
1033 state.update(|s| {
1035 s.ids
1036 .stat_icons
1037 .resize(STATS.len(), &mut ui.widget_id_generator())
1038 });
1039 state.update(|s| {
1040 s.ids
1041 .stat_txts
1042 .resize(STATS.len(), &mut ui.widget_id_generator())
1043 });
1044 let combat_rating = combat_rating(
1046 inventory,
1047 self.health,
1048 self.energy,
1049 self.poise,
1050 self.skill_set,
1051 *self.body,
1052 self.msm,
1053 )
1054 .min(999.9);
1055 let indicator_col = cr_color(combat_rating);
1056 for i in STATS.iter().copied().enumerate() {
1057 let btn = Button::image(match i.1 {
1058 "Health" => self.imgs.health_ico,
1059 "Energy" => self.imgs.energy_ico,
1060 "Combat Rating" => self.imgs.combat_rating_ico,
1061 "Protection" => self.imgs.protection_ico,
1062 "Stun Resilience" => self.imgs.stun_res_ico,
1063 "Stealth" => self.imgs.stealth_rating_ico,
1064 _ => self.imgs.nothing,
1065 })
1066 .w_h(20.0, 20.0)
1067 .image_color(if i.1 == "Combat Rating" {
1068 indicator_col
1069 } else {
1070 TEXT_COLOR
1071 });
1072 let protection_txt = format!(
1073 "{}%",
1074 (100.0
1075 * Damage::compute_damage_reduction(
1076 None,
1077 Some(inventory),
1078 Some(self.stats),
1079 self.msm
1080 )) as i32
1081 );
1082 let health_txt = format!("{}", self.health.maximum().round() as usize);
1083 let energy_txt = format!("{}", self.energy.maximum().round() as usize);
1084 let combat_rating_txt = format!("{}", (combat_rating * 10.0) as usize);
1085 let stun_res_txt = format!(
1086 "{}",
1087 (100.0
1088 * Poise::compute_poise_damage_reduction(
1089 Some(inventory),
1090 self.msm,
1091 None,
1092 Some(self.stats),
1093 )) as i32
1094 );
1095 let stealth_txt = format!(
1096 "{:.1}%",
1097 ((1.0
1098 - perception_dist_multiplier_from_stealth(
1099 Some(inventory),
1100 None,
1101 self.msm
1102 ))
1103 * 100.0)
1104 );
1105 let btn = if i.0 == 0 {
1106 btn.top_left_with_margins_on(state.bg_ids.bg_frame, 55.0, 10.0)
1107 } else {
1108 btn.down_from(state.ids.stat_icons[i.0 - 1], 7.0)
1109 };
1110 let tooltip_head = match i.1 {
1111 "Health" => i18n.get_msg("hud-bag-health"),
1112 "Energy" => i18n.get_msg("hud-bag-energy"),
1113 "Combat Rating" => i18n.get_msg("hud-bag-combat_rating"),
1114 "Protection" => i18n.get_msg("hud-bag-protection"),
1115 "Stun Resilience" => i18n.get_msg("hud-bag-stun_res"),
1116 "Stealth" => i18n.get_msg("hud-bag-stealth"),
1117 _ => Cow::Borrowed(""),
1118 };
1119 let tooltip_txt = match i.1 {
1120 "Combat Rating" => i18n.get_msg("hud-bag-combat_rating_desc"),
1121 "Protection" => i18n.get_msg("hud-bag-protection_desc"),
1122 "Stun Resilience" => i18n.get_msg("hud-bag-stun_res_desc"),
1123 _ => Cow::Borrowed(""),
1124 };
1125 btn.with_tooltip(
1126 self.tooltip_manager,
1127 &tooltip_head,
1128 &tooltip_txt,
1129 &bag_tooltip,
1130 TEXT_COLOR,
1131 )
1132 .set(state.ids.stat_icons[i.0], ui);
1133 Text::new(match i.1 {
1134 "Health" => &health_txt,
1135 "Energy" => &energy_txt,
1136 "Combat Rating" => &combat_rating_txt,
1137 "Protection" => &protection_txt,
1138 "Stun Resilience" => &stun_res_txt,
1139 "Stealth" => &stealth_txt,
1140 _ => "",
1141 })
1142 .right_from(state.ids.stat_icons[i.0], 10.0)
1143 .font_id(self.fonts.cyri.conrod_id)
1144 .font_size(self.fonts.cyri.scale(14))
1145 .color(TEXT_COLOR)
1146 .graphics_for(state.ids.stat_icons[i.0])
1147 .set(state.ids.stat_txts[i.0], ui);
1148 }
1149 let item_slot = EquipSlot::Armor(ArmorSlot::Head);
1152 let slot = slot_maker
1153 .fabricate(item_slot, [45.0; 2])
1154 .mid_top_with_margin_on(state.bg_ids.bg_frame, 60.0)
1155 .with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
1156 .filled_slot(filled_slot);
1157
1158 let slot_id = state.ids.head_slot;
1159 set_tooltip!(slot, slot_id, item_slot, "hud-bag-head");
1160
1161 let item_slot = EquipSlot::Armor(ArmorSlot::Neck);
1163 let slot = slot_maker
1164 .fabricate(item_slot, [45.0; 2])
1165 .mid_bottom_with_margin_on(state.ids.head_slot, -55.0)
1166 .with_icon(self.imgs.necklace_bg, Vec2::new(40.0, 31.0), Some(UI_MAIN))
1167 .filled_slot(filled_slot);
1168
1169 let slot_id = state.ids.neck_slot;
1170 set_tooltip!(slot, slot_id, item_slot, "hud-bag-neck");
1171
1172 let item_slot = EquipSlot::Armor(ArmorSlot::Chest);
1175 let slot = slot_maker
1176 .fabricate(item_slot, [85.0; 2])
1177 .mid_bottom_with_margin_on(state.ids.neck_slot, -95.0)
1178 .with_icon(self.imgs.chest_bg, Vec2::new(64.0, 42.0), Some(UI_MAIN))
1179 .filled_slot(filled_slot);
1180
1181 let slot_id = state.ids.chest_slot;
1182 set_tooltip!(slot, slot_id, item_slot, "hud-bag-chest");
1183
1184 let item_slot = EquipSlot::Armor(ArmorSlot::Shoulders);
1186 let slot = slot_maker
1187 .fabricate(item_slot, [70.0; 2])
1188 .bottom_left_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
1189 .with_icon(self.imgs.shoulders_bg, Vec2::new(60.0, 36.0), Some(UI_MAIN))
1190 .filled_slot(filled_slot);
1191
1192 let slot_id = state.ids.shoulders_slot;
1193 set_tooltip!(slot, slot_id, item_slot, "hud-bag-shoulders");
1194
1195 let item_slot = EquipSlot::Armor(ArmorSlot::Hands);
1197 let slot = slot_maker
1198 .fabricate(item_slot, [70.0; 2])
1199 .bottom_right_with_margins_on(state.ids.chest_slot, 0.0, -80.0)
1200 .with_icon(self.imgs.hands_bg, Vec2::new(55.0, 60.0), Some(UI_MAIN))
1201 .filled_slot(filled_slot);
1202
1203 let slot_id = state.ids.hands_slot;
1204 set_tooltip!(slot, slot_id, item_slot, "hud-bag-hands");
1205
1206 let item_slot = EquipSlot::Armor(ArmorSlot::Belt);
1208 let slot = slot_maker
1209 .fabricate(item_slot, [45.0; 2])
1210 .mid_bottom_with_margin_on(state.ids.chest_slot, -55.0)
1211 .with_icon(self.imgs.belt_bg, Vec2::new(40.0, 23.0), Some(UI_MAIN))
1212 .filled_slot(filled_slot);
1213
1214 let slot_id = state.ids.belt_slot;
1215 set_tooltip!(slot, slot_id, item_slot, "hud-bag-belt");
1216
1217 let item_slot = EquipSlot::Armor(ArmorSlot::Legs);
1219 let slot = slot_maker
1220 .fabricate(item_slot, [85.0; 2])
1221 .mid_bottom_with_margin_on(state.ids.belt_slot, -95.0)
1222 .with_icon(self.imgs.legs_bg, Vec2::new(48.0, 70.0), Some(UI_MAIN))
1223 .filled_slot(filled_slot);
1224
1225 let slot_id = state.ids.legs_slot;
1226 set_tooltip!(slot, slot_id, item_slot, "hud-bag-legs");
1227
1228 let item_slot = EquipSlot::Armor(ArmorSlot::Ring1);
1230 let slot = slot_maker
1231 .fabricate(item_slot, [45.0; 2])
1232 .bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0)
1233 .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
1234 .filled_slot(filled_slot);
1235
1236 let slot_id = state.ids.ring1_slot;
1237 set_tooltip!(slot, slot_id, item_slot, "hud-bag-ring");
1238
1239 let item_slot = EquipSlot::Armor(ArmorSlot::Ring2);
1241 let slot = slot_maker
1242 .fabricate(item_slot, [45.0; 2])
1243 .bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0)
1244 .with_icon(self.imgs.ring_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN))
1245 .filled_slot(filled_slot);
1246
1247 let slot_id = state.ids.ring2_slot;
1248 set_tooltip!(slot, slot_id, item_slot, "hud-bag-ring");
1249
1250 let item_slot = EquipSlot::Armor(ArmorSlot::Back);
1252 let slot = slot_maker
1253 .fabricate(item_slot, [45.0; 2])
1254 .down_from(state.ids.ring2_slot, 10.0)
1255 .with_icon(self.imgs.back_bg, Vec2::new(33.0, 40.0), Some(UI_MAIN))
1256 .filled_slot(filled_slot);
1257
1258 let slot_id = state.ids.back_slot;
1259 set_tooltip!(slot, slot_id, item_slot, "hud-bag-back");
1260
1261 let item_slot = EquipSlot::Armor(ArmorSlot::Feet);
1263 let slot = slot_maker
1264 .fabricate(item_slot, [45.0; 2])
1265 .down_from(state.ids.ring1_slot, 10.0)
1266 .with_icon(self.imgs.feet_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN))
1267 .filled_slot(filled_slot);
1268
1269 let slot_id = state.ids.feet_slot;
1270 set_tooltip!(slot, slot_id, item_slot, "hud-bag-feet");
1271
1272 let item_slot = EquipSlot::Lantern;
1274 let slot = slot_maker
1275 .fabricate(item_slot, [45.0; 2])
1276 .top_right_with_margins_on(state.bg_ids.bg_frame, 60.0, 5.0)
1277 .with_icon(self.imgs.lantern_bg, Vec2::new(24.0, 38.0), Some(UI_MAIN))
1278 .filled_slot(filled_slot);
1279
1280 let slot_id = state.ids.lantern_slot;
1281 set_tooltip!(slot, slot_id, item_slot, "hud-bag-lantern");
1282
1283 let item_slot = EquipSlot::Glider;
1285 let slot = slot_maker
1286 .fabricate(item_slot, [45.0; 2])
1287 .down_from(state.ids.lantern_slot, 5.0)
1288 .with_icon(self.imgs.glider_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN))
1289 .filled_slot(filled_slot);
1290
1291 let slot_id = state.ids.glider_slot;
1292 set_tooltip!(slot, slot_id, item_slot, "hud-bag-glider");
1293
1294 let item_slot = EquipSlot::Armor(ArmorSlot::Tabard);
1296 let slot = slot_maker
1297 .fabricate(item_slot, [45.0; 2])
1298 .down_from(state.ids.glider_slot, 5.0)
1299 .with_icon(self.imgs.tabard_bg, Vec2::new(38.0, 38.0), Some(UI_MAIN))
1300 .filled_slot(filled_slot);
1301
1302 let slot_id = state.ids.tabard_slot;
1303 set_tooltip!(slot, slot_id, item_slot, "hud-bag-tabard");
1304
1305 let item_slot = EquipSlot::ActiveMainhand;
1307 let slot = slot_maker
1308 .fabricate(item_slot, [85.0; 2])
1309 .bottom_right_with_margins_on(state.ids.back_slot, -95.0, 0.0)
1310 .with_icon(self.imgs.mainhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
1311 .filled_slot(filled_slot);
1312
1313 let slot_id = state.ids.active_mainhand_slot;
1314 set_tooltip!(slot, slot_id, item_slot, "hud-bag-mainhand");
1315
1316 let item_slot = EquipSlot::ActiveOffhand;
1318 let slot = slot_maker
1319 .fabricate(item_slot, [85.0; 2])
1320 .bottom_left_with_margins_on(state.ids.feet_slot, -95.0, 0.0)
1321 .with_icon(self.imgs.offhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN))
1322 .filled_slot(filled_slot);
1323
1324 let slot_id = state.ids.active_offhand_slot;
1325 set_tooltip!(slot, slot_id, item_slot, "hud-bag-offhand");
1326
1327 let item_slot = EquipSlot::InactiveMainhand;
1329 let slot = slot_maker
1330 .fabricate(item_slot, [40.0; 2])
1331 .bottom_right_with_margins_on(state.ids.active_mainhand_slot, 3.0, -47.0)
1332 .with_icon(self.imgs.mainhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
1333 .filled_slot(filled_slot);
1334
1335 let slot_id = state.ids.inactive_mainhand_slot;
1336 set_tooltip!(slot, slot_id, item_slot, "hud-bag-inactive_mainhand");
1337
1338 let item_slot = EquipSlot::InactiveOffhand;
1340 let slot = slot_maker
1341 .fabricate(item_slot, [40.0; 2])
1342 .bottom_left_with_margins_on(state.ids.active_offhand_slot, 3.0, -47.0)
1343 .with_icon(self.imgs.offhand_bg, Vec2::new(35.0, 35.0), Some(UI_MAIN))
1344 .filled_slot(filled_slot);
1345
1346 let slot_id = state.ids.inactive_offhand_slot;
1347 set_tooltip!(slot, slot_id, item_slot, "hud-bag-inactive_offhand");
1348
1349 if Button::image(self.imgs.swap_equipped_weapons_btn)
1350 .hover_image(self.imgs.swap_equipped_weapons_btn_hover)
1351 .press_image(self.imgs.swap_equipped_weapons_btn_press)
1352 .w_h(32.0, 40.0)
1353 .bottom_left_with_margins_on(state.bg_ids.bg_frame, 0.0, 23.3)
1354 .align_middle_y_of(state.ids.active_mainhand_slot)
1355 .with_tooltip(
1356 self.tooltip_manager,
1357 &i18n.get_msg("hud-bag-swap_equipped_weapons_title"),
1358 &(if let Some(key) = self
1359 .global_state
1360 .settings
1361 .controls
1362 .get_binding(GameInput::SwapLoadout)
1363 {
1364 i18n.get_msg_ctx(
1365 "hud-bag-swap_equipped_weapons_desc",
1366 &i18n::fluent_args! {
1367 "key" => key.display_string(key_layout)
1368 },
1369 )
1370 } else {
1371 Cow::Borrowed("")
1372 }),
1373 &tooltip,
1374 color::WHITE,
1375 )
1376 .set(state.ids.swap_equipped_weapons_btn, ui)
1377 .was_clicked()
1378 {
1379 event = Some(Event::SwapEquippedWeapons);
1380 }
1381 }
1382
1383 let item_slot = EquipSlot::Armor(ArmorSlot::Bag1);
1385 let slot = slot_maker
1386 .fabricate(item_slot, [35.0; 2])
1387 .bottom_left_with_margins_on(
1388 state.bg_ids.bg_frame,
1389 if self.show.bag_inv { 600.0 } else { 167.0 },
1390 3.0,
1391 )
1392 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1393 .filled_slot(filled_slot);
1394
1395 let slot_id = state.ids.bag1_slot;
1396 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1397
1398 let item_slot = EquipSlot::Armor(ArmorSlot::Bag2);
1400 let slot = slot_maker
1401 .fabricate(item_slot, [35.0; 2])
1402 .down_from(state.ids.bag1_slot, 2.0)
1403 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1404 .filled_slot(filled_slot);
1405
1406 let slot_id = state.ids.bag2_slot;
1407 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1408
1409 let item_slot = EquipSlot::Armor(ArmorSlot::Bag3);
1411 let slot = slot_maker
1412 .fabricate(item_slot, [35.0; 2])
1413 .down_from(state.ids.bag2_slot, 2.0)
1414 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1415 .filled_slot(filled_slot);
1416
1417 let slot_id = state.ids.bag3_slot;
1418 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1419
1420 let item_slot = EquipSlot::Armor(ArmorSlot::Bag4);
1422 let slot = slot_maker
1423 .fabricate(item_slot, [35.0; 2])
1424 .down_from(state.ids.bag3_slot, 2.0)
1425 .with_icon(self.imgs.bag_bg, Vec2::new(28.0, 24.0), Some(UI_MAIN))
1426 .filled_slot(filled_slot);
1427
1428 let slot_id = state.ids.bag4_slot;
1429 set_tooltip!(slot, slot_id, item_slot, "hud-bag-bag");
1430
1431 if Button::image(self.imgs.close_btn)
1433 .w_h(24.0, 25.0)
1434 .hover_image(self.imgs.close_btn_hover)
1435 .press_image(self.imgs.close_btn_press)
1436 .top_right_with_margins_on(state.bg_ids.bg, 0.0, 0.0)
1437 .set(state.ids.bag_close, ui)
1438 .was_clicked()
1439 {
1440 event = Some(Event::Close);
1441 }
1442 event
1443 }
1444}