    001package net.minecraft.tileentity;
    002
    003import cpw.mods.fml.common.registry.GameRegistry;
    004import cpw.mods.fml.relauncher.Side;
    005import cpw.mods.fml.relauncher.SideOnly;
    006import net.minecraft.block.Block;
    007import net.minecraft.block.BlockFurnace;
    008import net.minecraft.block.material.Material;
    009import net.minecraft.entity.player.EntityPlayer;
    010import net.minecraft.inventory.IInventory;
    011import net.minecraft.item.Item;
    012import net.minecraft.item.ItemBlock;
    013import net.minecraft.item.ItemHoe;
    014import net.minecraft.item.ItemStack;
    015import net.minecraft.item.ItemSword;
    016import net.minecraft.item.ItemTool;
    017import net.minecraft.item.crafting.FurnaceRecipes;
    018import net.minecraft.nbt.NBTTagCompound;
    019import net.minecraft.nbt.NBTTagList;
    020
    021import net.minecraftforge.common.ForgeDirection;
    022import net.minecraftforge.common.ISidedInventory;
    023
    024public class TileEntityFurnace extends TileEntity implements IInventory, ISidedInventory
    025{
    026    /**
    027     * The ItemStacks that hold the items currently being used in the furnace
    028     */
    029    private ItemStack[] furnaceItemStacks = new ItemStack[3];
    030
    031    /** The number of ticks that the furnace will keep burning */
    032    public int furnaceBurnTime = 0;
    033
    034    /**
    035     * The number of ticks that a fresh copy of the currently-burning item would keep the furnace burning for
    036     */
    037    public int currentItemBurnTime = 0;
    038
    039    /** The number of ticks that the current item has been cooking for */
    040    public int furnaceCookTime = 0;
    041
    042    /**
    043     * Returns the number of slots in the inventory.
    044     */
    045    public int getSizeInventory()
    046    {
    047        return this.furnaceItemStacks.length;
    048    }
    049
    050    /**
    051     * Returns the stack in slot i
    052     */
    053    public ItemStack getStackInSlot(int par1)
    054    {
    055        return this.furnaceItemStacks[par1];
    056    }
    057
    058    /**
    059     * Removes from an inventory slot (first arg) up to a specified number (second arg) of items and returns them in a
    060     * new stack.
    061     */
    062    public ItemStack decrStackSize(int par1, int par2)
    063    {
    064        if (this.furnaceItemStacks[par1] != null)
    065        {
    066            ItemStack var3;
    067
    068            if (this.furnaceItemStacks[par1].stackSize <= par2)
    069            {
    070                var3 = this.furnaceItemStacks[par1];
    071                this.furnaceItemStacks[par1] = null;
    072                return var3;
    073            }
    074            else
    075            {
    076                var3 = this.furnaceItemStacks[par1].splitStack(par2);
    077
    078                if (this.furnaceItemStacks[par1].stackSize == 0)
    079                {
    080                    this.furnaceItemStacks[par1] = null;
    081                }
    082
    083                return var3;
    084            }
    085        }
    086        else
    087        {
    088            return null;
    089        }
    090    }
    091
    092    /**
    093     * When some containers are closed they call this on each slot, then drop whatever it returns as an EntityItem -
    094     * like when you close a workbench GUI.
    095     */
    096    public ItemStack getStackInSlotOnClosing(int par1)
    097    {
    098        if (this.furnaceItemStacks[par1] != null)
    099        {
    100            ItemStack var2 = this.furnaceItemStacks[par1];
    101            this.furnaceItemStacks[par1] = null;
    102            return var2;
    103        }
    104        else
    105        {
    106            return null;
    107        }
    108    }
    109
    110    /**
    111     * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections).
    112     */
    113    public void setInventorySlotContents(int par1, ItemStack par2ItemStack)
    114    {
    115        this.furnaceItemStacks[par1] = par2ItemStack;
    116
    117        if (par2ItemStack != null && par2ItemStack.stackSize > this.getInventoryStackLimit())
    118        {
    119            par2ItemStack.stackSize = this.getInventoryStackLimit();
    120        }
    121    }
    122
    123    /**
    124     * Returns the name of the inventory.
    125     */
    126    public String getInvName()
    127    {
    128        return "container.furnace";
    129    }
    130
    131    /**
    132     * Reads a tile entity from NBT.
    133     */
    134    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    135    {
    136        super.readFromNBT(par1NBTTagCompound);
    137        NBTTagList var2 = par1NBTTagCompound.getTagList("Items");
    138        this.furnaceItemStacks = new ItemStack[this.getSizeInventory()];
    139
    140        for (int var3 = 0; var3 < var2.tagCount(); ++var3)
    141        {
    142            NBTTagCompound var4 = (NBTTagCompound)var2.tagAt(var3);
    143            byte var5 = var4.getByte("Slot");
    144
    145            if (var5 >= 0 && var5 < this.furnaceItemStacks.length)
    146            {
    147                this.furnaceItemStacks[var5] = ItemStack.loadItemStackFromNBT(var4);
    148            }
    149        }
    150
    151        this.furnaceBurnTime = par1NBTTagCompound.getShort("BurnTime");
    152        this.furnaceCookTime = par1NBTTagCompound.getShort("CookTime");
    153        this.currentItemBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);
    154    }
    155
    156    /**
    157     * Writes a tile entity to NBT.
    158     */
    159    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    160    {
    161        super.writeToNBT(par1NBTTagCompound);
    162        par1NBTTagCompound.setShort("BurnTime", (short)this.furnaceBurnTime);
    163        par1NBTTagCompound.setShort("CookTime", (short)this.furnaceCookTime);
    164        NBTTagList var2 = new NBTTagList();
    165
    166        for (int var3 = 0; var3 < this.furnaceItemStacks.length; ++var3)
    167        {
    168            if (this.furnaceItemStacks[var3] != null)
    169            {
    170                NBTTagCompound var4 = new NBTTagCompound();
    171                var4.setByte("Slot", (byte)var3);
    172                this.furnaceItemStacks[var3].writeToNBT(var4);
    173                var2.appendTag(var4);
    174            }
    175        }
    176
    177        par1NBTTagCompound.setTag("Items", var2);
    178    }
    179
    180    /**
    181     * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. *Isn't
    182     * this more of a set than a get?*
    183     */
    184    public int getInventoryStackLimit()
    185    {
    186        return 64;
    187    }
    188
    189    @SideOnly(Side.CLIENT)
    190
    191    /**
    192     * Returns an integer between 0 and the passed value representing how close the current item is to being completely
    193     * cooked
    194     */
    195    public int getCookProgressScaled(int par1)
    196    {
    197        return this.furnaceCookTime * par1 / 200;
    198    }
    199
    200    @SideOnly(Side.CLIENT)
    201
    202    /**
    203     * Returns an integer between 0 and the passed value representing how much burn time is left on the current fuel
    204     * item, where 0 means that the item is exhausted and the passed value means that the item is fresh
    205     */
    206    public int getBurnTimeRemainingScaled(int par1)
    207    {
    208        if (this.currentItemBurnTime == 0)
    209        {
    210            this.currentItemBurnTime = 200;
    211        }
    212
    213        return this.furnaceBurnTime * par1 / this.currentItemBurnTime;
    214    }
    215
    216    /**
    217     * Returns true if the furnace is currently burning
    218     */
    219    public boolean isBurning()
    220    {
    221        return this.furnaceBurnTime > 0;
    222    }
    223
    224    /**
    225     * Allows the entity to update its state. Overridden in most subclasses, e.g. the mob spawner uses this to count
    226     * ticks and creates a new spawn inside its implementation.
    227     */
    228    public void updateEntity()
    229    {
    230        boolean var1 = this.furnaceBurnTime > 0;
    231        boolean var2 = false;
    232
    233        if (this.furnaceBurnTime > 0)
    234        {
    235            --this.furnaceBurnTime;
    236        }
    237
    238        if (!this.worldObj.isRemote)
    239        {
    240            if (this.furnaceBurnTime == 0 && this.canSmelt())
    241            {
    242                this.currentItemBurnTime = this.furnaceBurnTime = getItemBurnTime(this.furnaceItemStacks[1]);
    243
    244                if (this.furnaceBurnTime > 0)
    245                {
    246                    var2 = true;
    247
    248                    if (this.furnaceItemStacks[1] != null)
    249                    {
    250                        --this.furnaceItemStacks[1].stackSize;
    251
    252                        if (this.furnaceItemStacks[1].stackSize == 0)
    253                        {
    254                            this.furnaceItemStacks[1] = this.furnaceItemStacks[1].getItem().getContainerItemStack(furnaceItemStacks[1]);
    255                        }
    256                    }
    257                }
    258            }
    259
    260            if (this.isBurning() && this.canSmelt())
    261            {
    262                ++this.furnaceCookTime;
    263
    264                if (this.furnaceCookTime == 200)
    265                {
    266                    this.furnaceCookTime = 0;
    267                    this.smeltItem();
    268                    var2 = true;
    269                }
    270            }
    271            else
    272            {
    273                this.furnaceCookTime = 0;
    274            }
    275
    276            if (var1 != this.furnaceBurnTime > 0)
    277            {
    278                var2 = true;
    279                BlockFurnace.updateFurnaceBlockState(this.furnaceBurnTime > 0, this.worldObj, this.xCoord, this.yCoord, this.zCoord);
    280            }
    281        }
    282
    283        if (var2)
    284        {
    285            this.onInventoryChanged();
    286        }
    287    }
    288
    289    /**
    290     * Returns true if the furnace can smelt an item, i.e. has a source item, destination stack isn't full, etc.
    291     */
    292    private boolean canSmelt()
    293    {
    294        if (this.furnaceItemStacks[0] == null)
    295        {
    296            return false;
    297        }
    298        else
    299        {
    300            ItemStack var1 = FurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
    301            if (var1 == null) return false;
    302            if (this.furnaceItemStacks[2] == null) return true;
    303            if (!this.furnaceItemStacks[2].isItemEqual(var1)) return false;
    304            int result = furnaceItemStacks[2].stackSize + var1.stackSize;
    305            return (result <= getInventoryStackLimit() && result <= var1.getMaxStackSize());
    306        }
    307    }
    308
    309    /**
    310     * Turn one item from the furnace source stack into the appropriate smelted item in the furnace result stack
    311     */
    312    public void smeltItem()
    313    {
    314        if (this.canSmelt())
    315        {
    316            ItemStack var1 = FurnaceRecipes.smelting().getSmeltingResult(this.furnaceItemStacks[0]);
    317
    318            if (this.furnaceItemStacks[2] == null)
    319            {
    320                this.furnaceItemStacks[2] = var1.copy();
    321            }
    322            else if (this.furnaceItemStacks[2].isItemEqual(var1))
    323            {
    324                furnaceItemStacks[2].stackSize += var1.stackSize;
    325            }
    326
    327            --this.furnaceItemStacks[0].stackSize;
    328
    329            if (this.furnaceItemStacks[0].stackSize <= 0)
    330            {
    331                this.furnaceItemStacks[0] = null;
    332            }
    333        }
    334    }
    335
    336    /**
    337     * Returns the number of ticks that the supplied fuel item will keep the furnace burning, or 0 if the item isn't
    338     * fuel
    339     */
    340    public static int getItemBurnTime(ItemStack par0ItemStack)
    341    {
    342        if (par0ItemStack == null)
    343        {
    344            return 0;
    345        }
    346        else
    347        {
    348            int var1 = par0ItemStack.getItem().itemID;
    349            Item var2 = par0ItemStack.getItem();
    350
    351            if (par0ItemStack.getItem() instanceof ItemBlock && Block.blocksList[var1] != null)
    352            {
    353                Block var3 = Block.blocksList[var1];
    354
    355                if (var3 == Block.woodSingleSlab)
    356                {
    357                    return 150;
    358                }
    359
    360                if (var3.blockMaterial == Material.wood)
    361                {
    362                    return 300;
    363                }
    364            }
    365
    366            if (var2 instanceof ItemTool && ((ItemTool) var2).getToolMaterialName().equals("WOOD")) return 200;
    367            if (var2 instanceof ItemSword && ((ItemSword) var2).getToolMaterialName().equals("WOOD")) return 200;
    368            if (var2 instanceof ItemHoe && ((ItemHoe) var2).func_77842_f().equals("WOOD")) return 200;
    369            if (var1 == Item.stick.itemID) return 100;
    370            if (var1 == Item.coal.itemID) return 1600;
    371            if (var1 == Item.bucketLava.itemID) return 20000;
    372            if (var1 == Block.sapling.blockID) return 100;
    373            if (var1 == Item.blazeRod.itemID) return 2400;
    374            return GameRegistry.getFuelValue(par0ItemStack);
    375        }
    376    }
    377
    378    /**
    379     * Return true if item is a fuel source (getItemBurnTime() > 0).
    380     */
    381    public static boolean isItemFuel(ItemStack par0ItemStack)
    382    {
    383        return getItemBurnTime(par0ItemStack) > 0;
    384    }
    385
    386    /**
    387     * Do not make give this method the name canInteractWith because it clashes with Container
    388     */
    389    public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer)
    390    {
    391        return this.worldObj.getBlockTileEntity(this.xCoord, this.yCoord, this.zCoord) != this ? false : par1EntityPlayer.getDistanceSq((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D) <= 64.0D;
    392    }
    393
    394    public void openChest() {}
    395
    396    public void closeChest() {}
    397
    398    @Override
    399    public int getStartInventorySide(ForgeDirection side)
    400    {
    401        if (side == ForgeDirection.DOWN) return 1;
    402        if (side == ForgeDirection.UP) return 0;
    403        return 2;
    404    }
    405
    406    @Override
    407    public int getSizeInventorySide(ForgeDirection side)
    408    {
    409        return 1;
    410    }
    411}