package com.arkcraft.module.blocks.common.tile; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S35PacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; import net.minecraft.world.EnumSkyBlock; import com.arkcraft.module.crafting.common.handlers.ForgeCraftingHandler; import com.arkcraft.module.crafting.common.handlers.ForgeRecipe; public class TileInventoryForge extends TileEntity implements IForge { private ItemStack[] itemStacks = new ItemStack[getSlotCount()]; /** the currently active recipes */ private Map<ForgeRecipe, Integer> activeRecipes = new HashMap<ForgeRecipe, Integer>(); /** the ticks burning left */ private int burningTicks; public TileInventoryForge() { super(); } /** * return the remaining burn time of the fuel * * @return seconds remaining */ public int secondsOfFuelRemaining() { return burningTicks / 20; // 20 ticks per second } // This method is called every tick to update the tile entity, i.e. // - see if the fuel has run out, and if so turn the furnace "off" and // slowly uncook the current item (if any) // - see if any of the items have finished smelting // It runs both on the server and the client. @Override public void update() { List<ForgeRecipe> possibleRecipes = ForgeCraftingHandler .findPossibleRecipes(this); updateBurning(possibleRecipes); // LogHelper.info(burningTicks); if (this.isBurning() && possibleRecipes.size() > 0) { Iterator<Entry<ForgeRecipe, Integer>> it = activeRecipes.entrySet() .iterator(); while (it.hasNext()) { Entry<ForgeRecipe, Integer> e = it.next(); if (!possibleRecipes.contains(e.getKey())) { it.remove(); } } for (ForgeRecipe r : possibleRecipes) { if (!activeRecipes.containsKey(r)) activeRecipes.put(r, 0); } updateCookTimes(); updateInventory(); markDirty(); } else clearActiveRecipes(); if (worldObj.isRemote) { worldObj.markBlockForUpdate(pos); } worldObj.checkLightFor(EnumSkyBlock.BLOCK, pos); } private void updateBurning(List<ForgeRecipe> possibleRecipes) { if (burningTicks < 1) { for (int i = 0; i < itemStacks.length; i++) { ItemStack stack = itemStacks[i]; if (stack != null && ForgeCraftingHandler.isValidFuel(stack .getItem()) && possibleRecipes.size() > 0) { if (!worldObj.isRemote) { stack.stackSize--; if (stack.stackSize == 0) itemStacks[i] = null; } this.burningTicks += ForgeCraftingHandler.getBurnTime(stack .getItem()); break; } } } if (burningTicks > 0) this.burningTicks--; } private void updateInventory() { Iterator<Entry<ForgeRecipe, Integer>> it = activeRecipes.entrySet() .iterator(); while (it.hasNext()) { Entry<ForgeRecipe, Integer> e = it.next(); if (e.getValue() >= (e.getKey().getBurnTime() * this .getBurnFactor() * 20)) { it.remove(); ForgeRecipe r = e.getKey(); List<Item> input = new ArrayList<Item>(r.getInput()); Item output = r.getOutput(); int outputStack = -1; for (int i = 0; i < itemStacks.length; i++) { if (itemStacks[i] != null && itemStacks[i].getItem() .equals(output) && itemStacks[i].stackSize < this .getInventoryStackLimit()) { outputStack = i; break; } } if (outputStack == -1) { for (int i = 0; i < itemStacks.length; i++) { if (itemStacks[i] == null) { outputStack = i; break; } } } if (outputStack != -1) { for (int i = 0; i < this.itemStacks.length; i++) { ItemStack stack = itemStacks[i]; if (stack != null) { while (input.remove(stack.getItem()) && stack.stackSize > 0) { stack.stackSize--; } if (stack.stackSize == 0) itemStacks[i] = null; } } if (itemStacks[outputStack] == null) { ItemStack s = new ItemStack(output); itemStacks[outputStack] = s; } else { itemStacks[outputStack].stackSize++; } } } } } private void updateCookTimes() { Iterator<Entry<ForgeRecipe, Integer>> it = activeRecipes.entrySet() .iterator(); while (it.hasNext()) { Entry<ForgeRecipe, Integer> e = it.next(); activeRecipes.put(e.getKey(), e.getValue() + 1); } } // Gets the number of slots in the inventory @Override public int getSizeInventory() { return itemStacks.length; } // Gets the stack in the given slot @Override public ItemStack getStackInSlot(int i) { return itemStacks[i]; } /** * Removes some of the units from itemstack in the given slot, and returns * as a separate itemstack * * @param slotIndex * the slot number to remove the items from * @param count * the number of units to remove * @return a new itemstack containing the units removed from the slot */ @Override public ItemStack decrStackSize(int slotIndex, int count) { ItemStack itemStackInSlot = getStackInSlot(slotIndex); if (itemStackInSlot == null) return null; ItemStack itemStackRemoved; if (itemStackInSlot.stackSize <= count) { itemStackRemoved = itemStackInSlot; setInventorySlotContents(slotIndex, null); } else { itemStackRemoved = itemStackInSlot.splitStack(count); if (itemStackInSlot.stackSize == 0) { setInventorySlotContents(slotIndex, null); } } markDirty(); return itemStackRemoved; } // overwrites the stack in the given slotIndex with the given stack @Override public void setInventorySlotContents(int slotIndex, ItemStack itemstack) { itemStacks[slotIndex] = itemstack; if (itemstack != null && itemstack.stackSize > getInventoryStackLimit()) { itemstack.stackSize = getInventoryStackLimit(); } markDirty(); } // This is the maximum number if items allowed in each slot // This only affects things such as hoppers trying to insert items you need // to use the container to enforce this for players // inserting items via the gui @Override public int getInventoryStackLimit() { return 64; } // Return true if the given player is able to use this block. In this case // it checks that // 1) the world tileentity hasn't been replaced in the meantime, and // 2) the player isn't too far away from the centre of the block @Override public boolean isUseableByPlayer(EntityPlayer player) { if (this.worldObj.getTileEntity(this.pos) != this) return false; final double X_CENTRE_OFFSET = 0.5; final double Y_CENTRE_OFFSET = 0.5; final double Z_CENTRE_OFFSET = 0.5; final double MAXIMUM_DISTANCE_SQ = 8.0 * 8.0; return player.getDistanceSq(pos.getX() + X_CENTRE_OFFSET, pos.getY() + Y_CENTRE_OFFSET, pos.getZ() + Z_CENTRE_OFFSET) < MAXIMUM_DISTANCE_SQ; } // ------------------------------ // This is where you save any data that you don't want to lose when the tile // entity unloads // In this case, it saves the state of the furnace (burn time etc) and the // itemstacks stored in the fuel, input, and output slots @Override public void writeToNBT(NBTTagCompound parentNBT) { super.writeToNBT(parentNBT); // The super call is required to // save and load the tiles // location // // Save the stored item stacks // to use an analogy with Java, this code generates an array of hashmaps // The itemStack in each slot is converted to an NBTTagCompound, which // is effectively a hashmap of key->value pairs such // as slot=1, id=2353, count=1, etc // Each of these NBTTagCompound are then inserted into NBTTagList, which // is similar to an array. NBTTagList dataForAllSlots = new NBTTagList(); for (int i = 0; i < this.itemStacks.length; ++i) { if (this.itemStacks[i] != null) { NBTTagCompound dataForThisSlot = new NBTTagCompound(); dataForThisSlot.setByte("Slot", (byte) i); this.itemStacks[i].writeToNBT(dataForThisSlot); dataForAllSlots.appendTag(dataForThisSlot); } } // the array of hashmaps is then inserted into the parent hashmap for // the container parentNBT.setTag("Items", dataForAllSlots); // Save everything else parentNBT.setInteger("burnTime", this.burningTicks); NBTTagList nbtList = new NBTTagList(); for (Entry<ForgeRecipe, Integer> e : activeRecipes.entrySet()) { NBTTagCompound nbt = new NBTTagCompound(); nbt.setInteger("cookTime", e.getValue()); nbt.setString("recipeKey", e.getKey().toString()); nbtList.appendTag(nbt); } parentNBT.setTag("activeRecipes", nbtList); } // This is where you load the data that you saved in writeToNBT @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); // The super call is required to save // and load the tiles location final byte NBT_TYPE_COMPOUND = 10; // See NBTBase.createNewByType() for // a listing NBTTagList dataForAllSlots = nbt.getTagList("Items", NBT_TYPE_COMPOUND); Arrays.fill(itemStacks, null); // set all slots to empty for (int i = 0; i < dataForAllSlots.tagCount(); ++i) { NBTTagCompound dataForOneSlot = dataForAllSlots.getCompoundTagAt(i); byte slotNumber = dataForOneSlot.getByte("Slot"); if (slotNumber >= 0 && slotNumber < this.itemStacks.length) { this.itemStacks[slotNumber] = ItemStack .loadItemStackFromNBT(dataForOneSlot); } } // Load everything else. Trim the arrays (or pad with 0) to make sure // they have the correct number of elements this.burningTicks = nbt.getInteger("burnTime"); NBTTagList nbtList = nbt.getTagList("activeRecipes", NBT_TYPE_COMPOUND); for (int i = 0; i < nbtList.tagCount(); i++) { NBTTagCompound nbtR = nbtList.getCompoundTagAt(i); int cookTime = nbtR.getInteger("cookTime"); ForgeRecipe r = ForgeCraftingHandler.getForgeRecipe(nbtR .getString("recipeKey")); this.activeRecipes.put(r, cookTime); } } public void clearActiveRecipes() { this.activeRecipes = new HashMap<ForgeRecipe, Integer>(); } // When the world loads from disk, the server needs to send the TileEntity // information to the client // it uses getDescriptionPacket() and onDataPacket() to do this @Override public Packet getDescriptionPacket() { NBTTagCompound nbtTagCompound = new NBTTagCompound(); writeToNBT(nbtTagCompound); return new S35PacketUpdateTileEntity(this.pos, 0, nbtTagCompound); } @Override public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { readFromNBT(pkt.getNbtCompound()); } // ------------------------ // set all slots to empty @Override public void clear() { Arrays.fill(itemStacks, null); } // will add a key for this container to the lang file so we can name it in // the GUI @Override public String getName() { return "container.inventory_refining_forge.name"; } @Override public boolean hasCustomName() { return false; } // standard code to look up what the human-readable name is @Override public IChatComponent getDisplayName() { return this.hasCustomName() ? new ChatComponentText(this.getName()) : new ChatComponentTranslation( this.getName()); } // Fields are used to send non-inventory information from the server to // interested clients // The container code caches the fields and sends the client any fields // which have changed. // The field ID is limited to byte, and the field value is limited to short. // (if you use more than this, they get cast down // in the network packets) // If you need more than this, or shorts are too small, use a custom packet // in your container instead. // private static final byte COOK_FIELD_ID = 0; // private static final byte FIRST_BURN_TIME_REMAINING_FIELD_ID = 1; // private static final byte FIRST_BURN_TIME_INITIAL_FIELD_ID = // FIRST_BURN_TIME_REMAINING_FIELD_ID + (byte) FUEL_SLOTS_COUNT; // private static final byte NUMBER_OF_FIELDS = // FIRST_BURN_TIME_INITIAL_FIELD_ID + (byte) FUEL_SLOTS_COUNT; @Override public int getField(int id) { // if (id == COOK_FIELD_ID) return cookTime; // if (id >= FIRST_BURN_TIME_REMAINING_FIELD_ID && id < // FIRST_BURN_TIME_REMAINING_FIELD_ID + FUEL_SLOTS_COUNT) { return // burnTimeRemaining[id - FIRST_BURN_TIME_REMAINING_FIELD_ID]; } // if (id >= FIRST_BURN_TIME_INITIAL_FIELD_ID && id < // FIRST_BURN_TIME_INITIAL_FIELD_ID + FUEL_SLOTS_COUNT) { return // burnTimeInitialValue[id - FIRST_BURN_TIME_INITIAL_FIELD_ID]; } // System.err.println("Invalid field ID in TileInventorySmelting.getField:" // + id); return 0; } @Override public void setField(int id, int value) { // if (id == COOK_FIELD_ID) // { // cookTime = (short) value; // } // else if (id >= FIRST_BURN_TIME_REMAINING_FIELD_ID && id < // FIRST_BURN_TIME_REMAINING_FIELD_ID + FUEL_SLOTS_COUNT) // { // burnTimeRemaining[id - FIRST_BURN_TIME_REMAINING_FIELD_ID] = value; // } // else if (id >= FIRST_BURN_TIME_INITIAL_FIELD_ID && id < // FIRST_BURN_TIME_INITIAL_FIELD_ID + FUEL_SLOTS_COUNT) // { // burnTimeInitialValue[id - FIRST_BURN_TIME_INITIAL_FIELD_ID] = value; // } // else // { // System.err.println("Invalid field ID in TileInventorySmelting.setField:" // + id); // } } @Override public int getFieldCount() { return 0; // return NUMBER_OF_FIELDS; } // ----------------------------------------------------------------------------------------------------------- // The following methods are not needed for this example but are part of // IInventory so they must be implemented // Unused unless your container specifically uses it. // Return true if the given stack is allowed to go in the given slot @Override public boolean isItemValidForSlot(int slotIndex, ItemStack itemstack) { return true; } /** * This method removes the entire contents of the given slot and returns it. * Used by containers such as crafting tables which return any items in * their slots when you close the GUI * * @param slotIndex * @return */ @Override public ItemStack getStackInSlotOnClosing(int slotIndex) { ItemStack itemStack = getStackInSlot(slotIndex); if (itemStack != null) setInventorySlotContents(slotIndex, null); return itemStack; } @Override public void openInventory(EntityPlayer player) { } @Override public void closeInventory(EntityPlayer player) { } @Override public int getSlotCount() { return 8; } @Override public boolean isBurning() { return this.burningTicks > 0; } @Override public double getBurnFactor() { return 20d; } }