package codechicken.nei.handler; import codechicken.nei.util.NEIClientUtils; import codechicken.nei.util.helper.GuiHelper; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.inventory.ClickType; import net.minecraft.inventory.Container; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import static codechicken.nei.util.NEIServerUtils.*; public class FastTransferManager { /** * Based on the general assumption that we want to fill top to bottom, left to right */ public static class SlotPositionComparator implements Comparator<Integer> { Container container; public SlotPositionComparator(Container c) { container = c; } @Override public int compare(Integer arg0, Integer arg1) { Slot slot1 = container.getSlot(arg0); Slot slot2 = container.getSlot(arg1); if (slot2.yPos != slot1.yPos) { return slot1.yPos - slot2.yPos; } return slot1.xPos - slot2.xPos; } } public LinkedList<LinkedList<Integer>> slotZones = new LinkedList<>(); public HashMap<Integer, Integer> slotZoneMap = new HashMap<>(); private void generateSlotMap(Container container, ItemStack stack) { stack = stack.copy(); stack.setCount(1); for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++) { if (slotZoneMap.containsKey(slotNo) || !container.getSlot(slotNo).isItemValid(stack)) { continue; } HashSet<Integer> connectedSlots = new HashSet<>(); findConnectedSlots(container, slotNo, connectedSlots); LinkedList<Integer> zoneSlots = new LinkedList<>(connectedSlots); zoneSlots.sort(new SlotPositionComparator(container)); slotZones.add(zoneSlots); for (int i : zoneSlots) { slotZoneMap.put(i, slotZones.size() - 1); } } } private void findConnectedSlots(Container container, int slotNo, HashSet<Integer> connectedSlots) { connectedSlots.add(slotNo); Slot slot = container.getSlot(slotNo); final int threshold = 18; for (int i = 0; i < container.inventorySlots.size(); i++) { if (connectedSlots.contains(i)) { continue; } Slot slot1 = container.getSlot(i); if (Math.abs(slot.xPos - slot1.xPos) <= threshold && Math.abs(slot.yPos - slot1.yPos) <= threshold) { findConnectedSlots(container, i, connectedSlots); } } } public static int findSlotWithItem(Container container, ItemStack teststack) { for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++) { ItemStack stack = container.getSlot(slotNo).getStack(); if (!stack.isEmpty() && areStacksSameType(stack, teststack)) { return slotNo; } } return -1; } public static void clearSlots(Container container) { for (int slotNo = 0; slotNo < container.inventorySlots.size(); slotNo++) { container.inventorySlots.get(slotNo).putStack(ItemStack.EMPTY); } } public void performMassTransfer(GuiContainer window, int fromSlot, int toSlot, ItemStack heldStack) { generateSlotMap(window.inventorySlots, heldStack); Integer fromZone = slotZoneMap.get(fromSlot); Integer toZone = slotZoneMap.get(toSlot); if (fromZone == null || toZone == null || fromZone.equals(toZone)) { return; } if (!NEIClientUtils.getHeldItem().isEmpty() && !areStacksSameType(heldStack, NEIClientUtils.getHeldItem())) { return; } if (!fillZoneWithHeldItem(window, toZone)) { return; } for (int transferFrom : slotZones.get(fromZone)) { ItemStack transferStack = window.inventorySlots.getSlot(transferFrom).getStack(); if (!areStacksSameType(heldStack, transferStack)) { continue; } clickSlot(window, transferFrom); if (!fillZoneWithHeldItem(window, toZone)) { clickSlot(window, transferFrom); return; } } } /** * @return The slot that one item from the source slot will end up in apon shift clicking, -1 if none. */ public int findShiftClickDestinationSlot(Container container, int fromSlot) { LinkedList<ItemStack> save = saveContainer(container); Slot slot = container.getSlot(fromSlot); ItemStack stack = slot.getStack(); if (stack.isEmpty()) { return -1; } stack.setCount(1); slot.putStack(stack.copy()); LinkedList<ItemStack> compareBefore = saveContainer(container); container.slotClick(fromSlot, 0, ClickType.QUICK_MOVE, Minecraft.getMinecraft().player); LinkedList<ItemStack> compareAfter = saveContainer(container); try { //if(compareAfter.get(fromSlot) != null)//transfer failed //return -1; for (int i = 0; i < compareBefore.size(); i++) { if (i == fromSlot) { continue; } ItemStack before = compareBefore.get(i); ItemStack after = compareAfter.get(i); if (!areStacksIdentical(before, after) && !after.isEmpty()) { if (before.isEmpty() ? areStacksSameType(stack, after) ://transfered into this empty slot areStacksSameType(stack, after) && after.getCount() - before.getCount() > 0)//it added to this stack { return i; } } } return -1; } finally { restoreContainer(container, save); } } public LinkedList<ItemStack> saveContainer(Container container) { LinkedList<ItemStack> stacks = new LinkedList<>(); for (int i = 0; i < container.inventorySlots.size(); i++) { stacks.add(copyStack(container.getSlot(i).getStack())); } return stacks; } public void restoreContainer(Container container, LinkedList<ItemStack> items) { for (int i = 0; i < container.inventorySlots.size(); i++) { container.getSlot(i).putStack(items.get(i)); } container.slotClick(-999, 0, ClickType.PICKUP, Minecraft.getMinecraft().player); } public void transferItem(GuiContainer window, int fromSlot) { int toSlot = findShiftClickDestinationSlot(window.inventorySlots, fromSlot); if (toSlot == -1) { return; } Slot from = window.inventorySlots.getSlot(fromSlot); if (from.isItemValid(from.getStack())) { moveOneItem(window, fromSlot, toSlot); } else//slots that you can't put stuff in { moveOutputSet(window, fromSlot, toSlot); } } public void moveOutputSet(GuiContainer window, int fromSlot, int toSlot) { if (!NEIClientUtils.getHeldItem().isEmpty()) { return; } clickSlot(window, fromSlot);//pickup fromSlot if (NEIClientUtils.getHeldItem().isEmpty())//maybe this container does auto transfers. No need to pick up the final item { return; } clickSlot(window, toSlot);//place one in toSlot } public void moveOneItem(GuiContainer window, int fromSlot, int toSlot) { clickSlot(window, fromSlot);//pickup fromSlot clickSlot(window, toSlot, 1);//place one in toSlot clickSlot(window, fromSlot);//place down in fromSlot } public void retrieveItem(GuiContainer window, int toSlot) { Slot slot = window.inventorySlots.getSlot(toSlot); ItemStack slotStack = slot.getStack(); if (slotStack.isEmpty() || slotStack.getCount() == slot.getSlotStackLimit() || slotStack.getCount() == slotStack.getMaxStackSize()) { return; } generateSlotMap(window.inventorySlots, slotStack); Integer destZone = slotZoneMap.get(toSlot); if (destZone == null)//slots that don't accept { return; } int firstZoneSlot = findShiftClickDestinationSlot(window.inventorySlots, toSlot); int firstZone = -1; if (firstZoneSlot != -1) { Integer integer = slotZoneMap.get(firstZoneSlot); if (integer != null) { firstZone = integer; if (retrieveItemFromZone(window, firstZone, toSlot)) { return; } } } for (int zone = 0; zone < slotZones.size(); zone++) { if (zone == destZone || zone == firstZone) { continue; } if (retrieveItemFromZone(window, zone, toSlot)) { return; } } retrieveItemFromZone(window, destZone, toSlot); } private boolean retrieveItemFromZone(GuiContainer window, int zone, int toSlot) { ItemStack stack = window.inventorySlots.getSlot(toSlot).getStack(); for (int i : slotZones.get(zone)) { if (i == toSlot) { continue; } Slot slot = window.inventorySlots.getSlot(i); ItemStack stack1 = slot.getStack(); if (areStacksSameType(stack, stack1) && stack1.getCount() != slot.getSlotStackLimit() && //get from full stacks on second pass stack1.getCount() != stack1.getMaxStackSize()) { moveOneItem(window, i, toSlot); return true; } } for (int i : slotZones.get(zone)) { if (i == toSlot) { continue; } Slot slot = window.inventorySlots.getSlot(i); ItemStack stack1 = slot.getStack(); if (areStacksSameType(stack, stack1)) { moveOneItem(window, i, toSlot); return true; } } return false; } public static void clickSlot(GuiContainer window, int slotIndex) { clickSlot(window, slotIndex, 0); } public static void clickSlot(GuiContainer window, int slotIndex, int button) { clickSlot(window, slotIndex, button, ClickType.PICKUP); } public static void clickSlot(GuiContainer window, int slotIndex, int button, ClickType clickType) { GuiHelper.clickSlot(window, slotIndex, button, clickType); } private boolean fillZoneWithHeldItem(GuiContainer window, int zoneIndex) { for (int transferTo : slotZones.get(zoneIndex)) { ItemStack held = NEIClientUtils.getHeldItem(); if (held.isEmpty()) { break; } ItemStack inToSlot = window.inventorySlots.getSlot(transferTo).getStack(); if (!areStacksSameType(inToSlot, held)) { continue; } clickSlot(window, transferTo); } for (int transferTo : slotZones.get(zoneIndex))//repeat on empty slots { ItemStack held = NEIClientUtils.getHeldItem(); if (held.isEmpty()) { break; } ItemStack inToSlot = window.inventorySlots.getSlot(transferTo).getStack(); if (!inToSlot.isEmpty()) { continue; } clickSlot(window, transferTo); } return NEIClientUtils.getHeldItem().isEmpty(); } public void throwAll(GuiContainer window, int pickedUpFromSlot) { ItemStack held = NEIClientUtils.getHeldItem(); if (held.isEmpty()) { return; } clickSlot(window, -999); generateSlotMap(window.inventorySlots, held); Integer zone = slotZoneMap.get(pickedUpFromSlot); if (zone == null) //something went wrong and we can't work out where the item was picked up from { return; } for (int slotIndex : slotZones.get(zone)) { Slot slot = window.inventorySlots.getSlot(slotIndex); if (areStacksSameType(held, slot.getStack())) { clickSlot(window, slotIndex); clickSlot(window, -999); } } } }