steel_core/inventory/menu/
layout.rs1use std::{mem, range::Range, sync::Arc};
2
3use steel_registry::item_stack::ItemStack;
4
5use crate::{
6 inventory::{
7 lock::{ContainerLockGuard, ContainerRef},
8 menu::{
9 behavior::MenuBehavior,
10 builder::{FakeResultRemainderPolicy, Route},
11 },
12 },
13 player::Player,
14};
15
16pub(crate) struct MenuLayout {
18 pub(crate) routes: Vec<Route>,
19 pub(crate) drain_sections: Vec<Range<usize>>,
20}
21
22impl MenuLayout {
23 pub fn return_drained_items(
27 &self,
28 behavior: &MenuBehavior,
29 player: &Player,
30 return_to_inventory: bool,
31 ) {
32 if self.drain_sections.is_empty() {
33 return;
34 }
35
36 let mut guard = if return_to_inventory {
37 behavior.lock_all_containers_with(ContainerRef::from(Arc::clone(&player.inventory)))
38 } else {
39 behavior.lock_all_containers()
40 };
41 for range in &self.drain_sections {
42 for slot_index in *range {
43 let slot = &behavior.slots()[slot_index];
44 let item = mem::take(slot.get_item_mut(&mut guard));
45 if item.is_empty() {
46 continue;
47 }
48 slot.set_changed(&mut guard);
49 if return_to_inventory {
50 player.add_item_or_drop_with_guard(&mut guard, item);
51 } else {
52 let _ = guard.run_unlocked(|| player.drop_item(item, false, false));
53 }
54 }
55 }
56 }
57
58 pub fn quick_move(
62 &self,
63 behavior: &MenuBehavior,
64 guard: &mut ContainerLockGuard,
65 slot_index: usize,
66 player: &Player,
67 ) -> ItemStack {
68 let Some(route) = self.routes.iter().find(|r| r.from.contains(&slot_index)) else {
69 return ItemStack::empty();
70 };
71
72 let clicked = behavior.slots()[slot_index].get_item(guard).clone();
73 if clicked.is_empty() {
74 return ItemStack::empty();
75 }
76
77 if !behavior.slots()[slot_index].may_pickup(guard, player) {
79 return ItemStack::empty();
80 }
81
82 let mut remaining = clicked.clone();
83 let moved = route.targets.iter().any(|target| {
84 behavior.move_item_stack_to(
85 guard,
86 slot_index,
87 &mut remaining,
88 target.start,
89 target.end,
90 route.direction,
91 )
92 });
93 if !moved {
94 return ItemStack::empty();
95 }
96
97 behavior.update_quick_move_source(guard, slot_index, &remaining, &clicked);
98
99 if remaining.count == clicked.count {
101 return ItemStack::empty();
102 }
103
104 let slot = &behavior.slots()[slot_index];
105 if let Some(leftover) = slot.on_take(guard, &remaining, player) {
106 player.add_item_or_drop_with_guard(guard, leftover);
107 }
108 if slot.is_fake()
111 && !remaining.is_empty()
112 && route.fake_result_remainder == FakeResultRemainderPolicy::Drop
113 {
114 let _ = guard.run_unlocked(|| player.drop_item(remaining.clone(), false, false));
115 }
116
117 clicked
118 }
119}