steel_core/inventory/menu/kinds/
chest_menu.rs1use steel_registry::menu_type::MenuTypeRef;
9use steel_registry::vanilla_menu_types;
10
11use crate::inventory::prelude::*;
12use crate::player::player_inventory::PlayerInventory;
13
14#[must_use]
19pub fn chest(
20 inventory: Shared<PlayerInventory>,
21 container_id: u8,
22 container: impl Into<ContainerRef>,
23 rows: usize,
24) -> Menu {
25 let container = container.into();
26 chest_with_kind(
27 inventory,
28 container_id,
29 container.clone(),
30 rows,
31 ChestKind { container },
32 )
33}
34
35#[must_use]
44pub(crate) fn chest_with_kind(
45 inventory: Shared<PlayerInventory>,
46 container_id: u8,
47 container: impl Into<ContainerRef>,
48 rows: usize,
49 kind: impl MenuKind + 'static,
50) -> Menu {
51 let container = container.into();
52 assert!(
53 (1..=6).contains(&rows),
54 "Chest rows must be between 1 and 6"
55 );
56
57 let mut builder = MenuBuilder::new(menu_type_for_rows(rows), container_id);
58 let chest = builder.section(&container, rows * 9);
59 let player = builder.player_inventory(&inventory);
60
61 builder.route(chest, player.all(), FillDirection::Backward);
62 builder.route(player.all(), chest, FillDirection::Forward);
63
64 builder.build(kind)
65}
66
67#[must_use]
72pub fn menu_type_for_rows(rows: usize) -> MenuTypeRef {
73 match rows {
74 1 => &vanilla_menu_types::GENERIC_9X1,
75 2 => &vanilla_menu_types::GENERIC_9X2,
76 3 => &vanilla_menu_types::GENERIC_9X3,
77 4 => &vanilla_menu_types::GENERIC_9X4,
78 5 => &vanilla_menu_types::GENERIC_9X5,
79 6 => &vanilla_menu_types::GENERIC_9X6,
80 _ => panic!("Invalid row count: {rows}"),
81 }
82}
83
84pub struct ChestKind {
86 container: ContainerRef,
88}
89
90unsafe impl steel_utils::DowncastType for ChestKind {
93 const TYPE_KEY: steel_utils::DowncastTypeKey =
94 steel_utils::DowncastTypeKey::new("steel:menu/chest");
95}
96
97impl MenuKind for ChestKind {
98 fn still_valid(&self, _behavior: &MenuBehavior, player: &Player) -> bool {
100 self.container.still_valid(player)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use steel_utils::locks::IntoShared as _;
107
108 use super::*;
109 use crate::inventory::container::SimpleContainer;
110
111 #[test]
112 fn chest_uses_exactly_the_rows_requested_from_oversized_container() {
113 let inventory = PlayerInventory::new().into_shared();
114 let container = SimpleContainer::new(18).into_shared();
115
116 let menu = chest(inventory, 1, container, 1);
117
118 assert_eq!(menu.behavior().slot_count(), 9 + 36);
119 }
120}