001    package net.minecraft.tileentity;
002    
003    import cpw.mods.fml.common.registry.GameRegistry;
004    import cpw.mods.fml.relauncher.Side;
005    import cpw.mods.fml.relauncher.SideOnly;
006    import net.minecraft.block.Block;
007    import net.minecraft.block.BlockFurnace;
008    import net.minecraft.block.material.Material;
009    import net.minecraft.entity.player.EntityPlayer;
010    import net.minecraft.inventory.IInventory;
011    import net.minecraft.item.Item;
012    import net.minecraft.item.ItemBlock;
013    import net.minecraft.item.ItemHoe;
014    import net.minecraft.item.ItemStack;
015    import net.minecraft.item.ItemSword;
016    import net.minecraft.item.ItemTool;
017    import net.minecraft.item.crafting.FurnaceRecipes;
018    import net.minecraft.nbt.NBTTagCompound;
019    import net.minecraft.nbt.NBTTagList;
020    
021    import net.minecraftforge.common.ForgeDirection;
022    import net.minecraftforge.common.ISidedInventory;
023    
024    public 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).func_77825_f().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    }