2222import net .minecraft .client .gui .screens .inventory .AbstractContainerScreen ;
2323import net .minecraft .world .inventory .ClickType ;
2424import net .minecraft .world .inventory .Slot ;
25- import net .minecraft .world .item .ItemStack ;
25+ import net .minecraft .world .item .* ;
2626
2727import java .util .ArrayDeque ;
2828import java .util .ArrayList ;
@@ -136,6 +136,17 @@ protected void sortOnClient(int[] sortedIds) {
136136 }
137137 if (stacks [i ].isEmpty ()) doneSlashEmpty .set (slotCount + i ); // mark if it's empty
138138 }
139+
140+ // Bundles require special handling. Specifically, to perform a swap between the carried
141+ // item and the target slot, you normally use left-click (0), but if holding a bundle
142+ // you must use right-click (1).
143+ // It isn't possible to always use right-click because right-clicking a bundle on an empty
144+ // slot does nothing, and right-clicking on a stack while carrying nothing takes half.
145+ // The current workaround is to maintain a copy of the theoretical inventory state to inform
146+ // the click decision. This will break if items enter or leave the inventory unexpectedly.
147+ Item carriedItem = Items .AIR ;
148+ Item [] backingStacks = Arrays .stream (stacks .clone ()).map (ItemStack ::getItem ).toArray (Item []::new );
149+
139150 // Iterate all slots, with i as the target slot index
140151 // sortedIds[i] is therefore the origin slot
141152 for (int i = 0 ; i < slotCount ; i ++) {
@@ -149,8 +160,11 @@ protected void sortOnClient(int[] sortedIds) {
149160
150161 // This is where the action happens.
151162 // Pick up the stack at the origin slot.
152- InteractionManager .push (screenHelper .createClickEvent (inventorySlots [sortedIds [i ]], 0 , ClickType .PICKUP ));
153- doneSlashEmpty .set (slotCount + sortedIds [i ]); // Mark the origin slot as empty (because we picked the stack up, duh)
163+ Item temp = backingStacks [sortedIds [i ]];
164+ backingStacks [sortedIds [i ]] = carriedItem ;
165+ carriedItem = temp ;
166+ InteractionManager .push (screenHelper .createClickEvent (inventorySlots [sortedIds [i ]], 0 , ClickType .PICKUP ));
167+ doneSlashEmpty .set (slotCount + sortedIds [i ]); // Mark the origin slot as empty (because we picked the stack up, duh)
154168 currentStack = stacks [sortedIds [i ]]; // Save the stack we're currently working with
155169 Slot workingSlot = inventorySlots [sortedIds [i ]]; // A slot that we can use when fiddling around with swapping stacks
156170 int id = i ; // id will reflect the target slot in the following loop
@@ -170,6 +184,9 @@ protected void sortOnClient(int[] sortedIds) {
170184 if (currentStack .getCount () < stacks [id ].getCount ()) { // Clicking with a low stack on a full stack does nothing
171185 // The workaround is: click working slot, click target slot, click working slot, click target slot, click working slot
172186 Slot targetSlot = inventorySlots [id ];
187+ temp = backingStacks [id ];
188+ backingStacks [id ] = carriedItem ;
189+ carriedItem = temp ;
173190 InteractionManager .push (screenHelper .createClickEvent (workingSlot , 0 , ClickType .PICKUP ));
174191 InteractionManager .push (screenHelper .createClickEvent (targetSlot , 0 , ClickType .PICKUP ));
175192 InteractionManager .push (screenHelper .createClickEvent (workingSlot , 0 , ClickType .PICKUP ));
@@ -184,7 +201,18 @@ protected void sortOnClient(int[] sortedIds) {
184201 }
185202
186203 // swap the current stack with the target stack
187- InteractionManager .push (screenHelper .createClickEvent (inventorySlots [id ], 0 , ClickType .PICKUP ));
204+ if ((backingStacks [id ] instanceof BundleItem && !(carriedItem instanceof AirItem ))
205+ || (carriedItem instanceof BundleItem && !(backingStacks [id ] instanceof AirItem ))) {
206+ temp = backingStacks [id ];
207+ backingStacks [id ] = carriedItem ;
208+ carriedItem = temp ;
209+ InteractionManager .push (screenHelper .createClickEvent (inventorySlots [id ], 1 , ClickType .PICKUP ));
210+ } else {
211+ temp = backingStacks [id ];
212+ backingStacks [id ] = carriedItem ;
213+ carriedItem = temp ;
214+ InteractionManager .push (screenHelper .createClickEvent (inventorySlots [id ], 0 , ClickType .PICKUP ));
215+ }
188216 currentStack = stacks [id ];
189217 doneSlashEmpty .set (id ); // mark the current target as done
190218 // If the target that we just swapped with was empty before, then this breaks the chain.
0 commit comments