/* This file is part of Project-Zed. Project-Zed is free software: you can redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Project-Zed is * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along * with Project-Zed. If not, see <http://www.gnu.org/licenses/> */ package com.projectzed.mod.container; import com.hockeyhurd.hcorelib.api.math.TimeLapse; import com.hockeyhurd.hcorelib.api.tileentity.AbstractTile; import com.projectzed.mod.ProjectZed; import com.projectzed.mod.handler.PacketHandler; import com.projectzed.mod.handler.message.MessageTileEntityFabricationTable; import com.projectzed.mod.tileentity.TileEntityFabricationTable; import com.projectzed.mod.util.WorldUtils; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.*; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; import java.util.*; import java.util.Map.Entry; /** * Class containing container code for FabricationTable. * * @author hockeyhurd * @version Nov 22, 2014 */ public class ContainerFabricationTable extends Container implements ITileContainer { /** * The crafting matrix inventory (3x3). */ public InventoryCrafting craftMatrix = new InventoryCrafting(this, 3, 3); public IInventory craftResult = new InventoryCraftResult(); private TileEntityFabricationTable te; private final int NUM_SLOTS; private InventoryPlayer inv; /** * @param inv * @param te */ public ContainerFabricationTable(InventoryPlayer inv, TileEntityFabricationTable te) { this.te = te; this.inv = inv; this.NUM_SLOTS = te.getSizeInventory(); addSlots(inv, te); this.onCraftMatrixChanged(this.craftMatrix); } /** * Adds all slots, player and container. * * @param inv = inventory. * @param te = tile entity object. */ private void addSlots(InventoryPlayer inv, TileEntityFabricationTable te) { // Add crafting matrix for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { // this.addSlotToContainer(new Slot(this.craftMatrix, x + y * 3, 67 + y * 18, 6 + x * 18)); this.addSlotToContainer(new Slot(this.craftMatrix, x + y * 3, 67 + x * 18, 6 + y * 18)); ItemStack stack = te.getStackInSlot((x + y * 3)); if (stack != null && stack.stackSize > 0) this.craftMatrix.setInventorySlotContents(x + y * 3, stack); } } // Add crafting result. this.addSlotToContainer(new SlotCrafting(inv.player, this.craftMatrix, this.craftResult, craftMatrix.getSizeInventory(), 161, 24)); // Add 'chest' slots for (int y = 0; y < 6; y++) { for (int x = 0; x < 12; x++) { this.addSlotToContainer(new Slot(te, (x + y * 12) + 10, 12 + x * 18, 62 + y * 18)); } } // Adds the player inventory to table's gui. for (int y = 0; y < 3; y++) { for (int x = 0; x < 9; x++) { this.addSlotToContainer(new Slot(inv, (x + y * 9) + 9, 39 + x * 18, 174 + y * 18)); } } // Adds the player hotbar slots to the gui. for (int i = 0; i < 9; i++) { this.addSlotToContainer(new Slot(inv, i, 39 + i * 18, 232)); // 198 } } /** * Callback for when the crafting matrix is changed. */ @Override public void onCraftMatrixChanged(IInventory inv) { if (craftMatrix != null) { craftResult.setInventorySlotContents(craftMatrix.getSizeInventory(), CraftingManager.getInstance().findMatchingRecipe(craftMatrix, te.getWorld())); } super.onCraftMatrixChanged(inv); } /** * Method to sort inventory. * * @param sortType = sort method (1 for lowest item id to highest, 2 for reverse previous, 3 for a-z by stack name, 4 z-a by stack name). */ public void sortInventory(int sortType) { TimeLapse timeLapse = new TimeLapse(); HashMap<Integer, List<ItemStack>> map = new HashMap<Integer, List<ItemStack>>(); HashMap<String, List<ItemStack>> map2 = new HashMap<String, List<ItemStack>>(); int id = 0; String name = ""; for (int i = 10; i < te.getSizeInventory(); i++) { if (te.getStackInSlot(i) != null) { id = Item.getIdFromItem(te.getStackInSlot(i).getItem()); name = te.getStackInSlot(i).getDisplayName(); List<ItemStack> tempList = new ArrayList<ItemStack>(te.getSizeInventory() - 10); if (sortType <= 2 && map.containsKey(id)) tempList = map.get(id); else if (sortType > 2 && map2.containsKey(name)) tempList = map2.get(name); tempList.add(te.getStackInSlot(i)); if (sortType <= 2) map.put(id, tempList); else if (sortType > 2) map2.put(name, tempList); te.setInventorySlotContents(i, null); } } List<ItemStack> outputList = new ArrayList<ItemStack>(map.keySet().size()); if (sortType <= 2) { List<Integer> keys = new ArrayList<Integer>(map.keySet()); Collections.sort(keys); if (sortType == 2) Collections.reverse(keys); for (int i : keys) { if (map.containsKey(i) && map.get(i) != null && map.get(i).size() > 0) { for (ItemStack stack : map.get(i)) { outputList.add(stack); map.remove(i); } } } } else if (sortType > 2) { List<String> keys = new ArrayList<String>(map2.keySet()); Collections.sort(keys); if (sortType == 4) Collections.reverse(keys); for (String s : keys) { if (map2.containsKey(s) && map2.get(s) != null && map2.get(s).size() > 0) { for (ItemStack stack : map2.get(s)) { outputList.add(stack); map2.remove(s); } } } } for (int i = 0; i < outputList.size(); i++) { if (i + 10 <= this.te.getSizeInventory()) this.mergeItemStack(outputList.get(i), this.craftMatrix.getSizeInventory() + 1, this.NUM_SLOTS, false); } ProjectZed.logHelper.info("Completed sorting in " + timeLapse.getEffectiveTimeSince() + " ms!"); } /** * Clears crafting grid matrix. */ public void clearCraftingGrid() { for (int i = 0; i < this.craftMatrix.getSizeInventory(); i++) { if (this.craftMatrix.getStackInSlot(i) != null) { if (this.mergeItemStack(craftMatrix.getStackInSlot(i), 3 * 3 + 1, NUM_SLOTS, false)) { this.craftMatrix.setInventorySlotContents(i, null); this.te.setInventorySlotContents(i, null); } else { final int xCoord = te.getPos().getX(); final int yCoord = te.getPos().getY(); final int zCoord = te.getPos().getZ(); WorldUtils.addItemDrop(craftMatrix.getStackInSlot(i), te.getWorld(), xCoord, yCoord, zCoord); craftMatrix.setInventorySlotContents(i, null); } } } this.onCraftMatrixChanged(craftMatrix); } @Override public void fillCraftingGrid(ItemStack[][] stacks, int limitAmount) { if (te.getWorld().isRemote) { // Format: Item name -> StackNode { amount, slotIndexes, ItemStack (copy) } Map<String, StackNode> inventoryMapping = new HashMap<String, StackNode>(te.getSizeInventory() << 1); buildInventoryMap(inventoryMapping); if (inventoryMapping.isEmpty()) return; /* * We should only need to calculate occurances if limit amount is something other than '1' * Since this was already checked in the JEI transfer handler! */ if (limitAmount > 1) { Map<String, Integer> occurrencesMapping = new TreeMap<String, Integer>(); // Simulation: // Pass 1 for (int stackIndex = 0; stackIndex < stacks.length; stackIndex++) { if (stacks[stackIndex] == null || stacks[stackIndex].length == 0) continue; for (int itemOptionIndex = 0; itemOptionIndex < stacks[stackIndex].length; itemOptionIndex++) { if (stacks[stackIndex][itemOptionIndex] == null) continue; ItemStack stack = stacks[stackIndex][itemOptionIndex].copy(); // stack.stackSize = limitAmount; final String key = stack.getUnlocalizedName(); if (inventoryMapping.containsKey(key)) { if (!occurrencesMapping.containsKey(key)) occurrencesMapping.put(key, 1); else occurrencesMapping.put(key, occurrencesMapping.get(key) + 1); break; } } } // Pass 2 calculate limiting amount: for (Entry<String, Integer> entry : occurrencesMapping.entrySet()) { limitAmount = Math.min(limitAmount, (int) Math.floor(inventoryMapping.get(entry.getKey()).amount / entry.getValue())); limitAmount = Math.max(limitAmount, 1); inventoryMapping.get(entry.getKey()).amount -= limitAmount; } } // limitAmount = Math.max(limitAmount, 1); ProjectZed.logHelper.info("limitAmount:", limitAmount); // Do moving: for (int stackIndex = 0; stackIndex < stacks.length; stackIndex++) { if (stacks[stackIndex] == null || stacks[stackIndex].length == 0) continue; for (int itemOptionIndex = 0; itemOptionIndex < stacks[stackIndex].length; itemOptionIndex++) { if (stacks[stackIndex][itemOptionIndex] == null) continue; ItemStack stack = stacks[stackIndex][itemOptionIndex].copy(); stack.stackSize = limitAmount; ItemStack removeStack = stack.copy(); // if (removeItemStack(removeStack, false)) { if (removeItemStack(removeStack, inventoryMapping)) { stack.stackSize = limitAmount; craftMatrix.setInventorySlotContents(stackIndex, stack); te.setInventorySlotContents(stackIndex, stack); putStackInSlot(stackIndex, stack); break; } } } this.onCraftMatrixChanged(craftMatrix); detectAndSendChanges(); PacketHandler.INSTANCE.sendToServer(new MessageTileEntityFabricationTable(te, 2)); } else { for (int i = 0; i < craftMatrix.getSizeInventory(); i++) { if (stacks[i] == null) continue; final ItemStack stack = stacks[i][0]; // final ItemStack stack = te.getStackInSlot(i); // craftMatrix.setInventorySlotContents(i, stack); te.setInventorySlotContents(i, stack); putStackInSlot(i, stack); } onCraftMatrixChanged(craftMatrix); detectAndSendChanges(); // final Vector3<Double> vec = te.worldVec().getVector3d(); // PacketHandler.INSTANCE.sendToAllAround(new MessageTileEntityFabricationTable(te), // new NetworkRegistry.TargetPoint(te.getWorld().provider.getDimension(), vec.x, vec.y, vec.z, 16.0f)); } } @Deprecated private boolean removeItemStack(ItemStack stackToRemove, boolean simulate) { if (stackToRemove == null || stackToRemove.stackSize == 0) return false; for (int i = 10; i < te.getSizeInventory() && stackToRemove.stackSize > 0; i++) { final ItemStack stack = te.getStackInSlot(i); if (stack == null) continue; final ItemStack copyComp = stack.copy(); copyComp.stackSize = stackToRemove.stackSize; // if (stackToRemove.isItemEqual(stack)) { if (ItemStack.areItemStacksEqual(stackToRemove, copyComp)) { int grabAmount = Math.min(stack.stackSize, stackToRemove.stackSize); stackToRemove.stackSize -= grabAmount; if (!simulate) { stack.stackSize -= grabAmount; if (stack.stackSize == 0) te.setInventorySlotContents(i, null); } } } return stackToRemove.stackSize == 0; } private boolean removeItemStack(ItemStack stackToRemove, Map<String, StackNode> inventoryMap) { if (stackToRemove == null || stackToRemove.stackSize <= 0 || inventoryMap == null || inventoryMap.isEmpty()) return false; ItemStack removeStackCopy = stackToRemove.copy(); // List<Integer> slotList = inventoryMap.get(stackToRemove.getUnlocalizedName()).slotIndexes; final StackNode stackNode = inventoryMap.get(stackToRemove.getUnlocalizedName()); if (stackNode == null) return false; List<Integer> slotList = stackNode.slotIndexes; List<Integer> removeList = new LinkedList<Integer>(); for (int slotIndex : slotList) { ItemStack stack = te.getStackInSlot(slotIndex); removeStackCopy.stackSize = stack.stackSize; if (ItemStack.areItemStacksEqual(removeStackCopy, stack)) { final int grabAmount = Math.min(stack.stackSize, stackToRemove.stackSize); stackToRemove.stackSize -= grabAmount; stack.stackSize -= grabAmount; if (stack.stackSize == 0) { removeList.add(slotIndex); te.setInventorySlotContents(slotIndex, null); } } if (stackToRemove.stackSize == 0) break; } for (Integer i : removeList) { slotList.remove(i); } return stackToRemove == null || stackToRemove.stackSize == 0; } /** * Builds a Map of the tileentity's inventory. * * @param map Map to reference. */ private void buildInventoryMap(Map<String, StackNode> map) { if (map == null) return; // We need a fresh map! if (!map.isEmpty()) map.clear(); for (int i = 0; i < craftMatrix.getSizeInventory(); i++) { final ItemStack stack = craftMatrix.getStackInSlot(i); if (stack == null) continue; final String key = stack.getUnlocalizedName(); if (!map.containsKey(key)) map.put(key, new StackNode(stack.stackSize, i, stack)); else { final StackNode stackNode = map.get(key); stackNode.slotIndexes.add(i); stackNode.amount += stack.stackSize; } } for (int i = 10; i < te.getSizeInventory(); i++) { final ItemStack stack = te.getStackInSlot(i); if (stack == null) continue; final String key = stack.getUnlocalizedName(); if (!map.containsKey(key)) map.put(key, new StackNode(stack.stackSize, i, stack)); else { final StackNode stackNode = map.get(key); stackNode.slotIndexes.add(i); stackNode.amount += stack.stackSize; } } } @Override public void onContainerClosed(EntityPlayer player) { final boolean isClient = player.getEntityWorld().isRemote; if (isClient) { for (int i = 0; i < this.craftMatrix.getSizeInventory(); i++) { this.te.setInventorySlotContents(i, this.craftMatrix.getStackInSlot(i)); } PacketHandler.INSTANCE.sendToServer(new MessageTileEntityFabricationTable(te)); } else { for (int i = 0; i < this.craftMatrix.getSizeInventory(); i++) { craftMatrix.setInventorySlotContents(i, te.getStackInSlot(i)); } this.onCraftMatrixChanged(this.craftMatrix); } } @Override public boolean canInteractWith(EntityPlayer player) { return true; } @Override public void detectAndSendChanges() { super.detectAndSendChanges(); } @Override public boolean mergeItemStack(ItemStack stack, int start, int end, boolean reverse) { return super.mergeItemStack(stack, start, end, reverse); } /** * Player shift-clicking a slot. * * @see net.minecraft.inventory.Container#transferStackInSlot(net.minecraft.entity.player.EntityPlayer, int) */ @Override public ItemStack transferStackInSlot(EntityPlayer player, int index) { ItemStack itemstack = null; Slot slot = (Slot) this.inventorySlots.get(index); if (slot != null && slot.getHasStack()) { ItemStack itemstack1 = slot.getStack(); itemstack = itemstack1.copy(); if (index < 10) { if (!this.mergeItemStack(itemstack1, 10, te.getSizeInventory(), false)) return null; slot.onSlotChange(itemstack1, itemstack); } else if (index >= 10 && index < te.getSizeInventory()) { if (!this.mergeItemStack(itemstack1, te.getSizeInventory(), this.inventorySlots.size(), false)) return null; } else { if (!this.mergeItemStack(itemstack1, 10, te.getSizeInventory(), false)) return null; } if (itemstack1.stackSize == 0) slot.putStack((ItemStack) null); else slot.onSlotChanged(); if (itemstack1.stackSize == itemstack.stackSize) return null; slot.onPickupFromSlot(player, itemstack1); } return itemstack; } @Override public Container getContainer() { return this; } @Override public AbstractTile getTile() { return te; } private static class StackNode { int amount; ItemStack itemStack; List<Integer> slotIndexes; StackNode(int amount, ItemStack itemStack) { this.amount = amount; this.itemStack = itemStack; slotIndexes = new LinkedList<Integer>(); } StackNode(int amount, int index, ItemStack itemStack) { this.amount = amount; this.itemStack = itemStack; slotIndexes = new LinkedList<Integer>(); slotIndexes.add(index); } } }