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