001package net.minecraft.block;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import java.util.Random;
006import net.minecraft.block.material.Material;
007import net.minecraft.client.renderer.texture.IconRegister;
008import net.minecraft.creativetab.CreativeTabs;
009import net.minecraft.dispenser.BehaviorDefaultDispenseItem;
010import net.minecraft.dispenser.IBehaviorDispenseItem;
011import net.minecraft.dispenser.IBlockSource;
012import net.minecraft.dispenser.IPosition;
013import net.minecraft.dispenser.IRegistry;
014import net.minecraft.dispenser.PositionImpl;
015import net.minecraft.dispenser.RegistryDefaulted;
016import net.minecraft.entity.EntityLiving;
017import net.minecraft.entity.item.EntityItem;
018import net.minecraft.entity.player.EntityPlayer;
019import net.minecraft.inventory.Container;
020import net.minecraft.inventory.IInventory;
021import net.minecraft.item.ItemStack;
022import net.minecraft.nbt.NBTTagCompound;
023import net.minecraft.tileentity.TileEntity;
024import net.minecraft.tileentity.TileEntityDispenser;
025import net.minecraft.util.EnumFacing;
026import net.minecraft.util.Icon;
027import net.minecraft.world.World;
028
029public class BlockDispenser extends BlockContainer
030{
031    /** Registry for all dispense behaviors. */
032    public static final IRegistry dispenseBehaviorRegistry = new RegistryDefaulted(new BehaviorDefaultDispenseItem());
033    protected Random random = new Random();
034    @SideOnly(Side.CLIENT)
035    protected Icon furnaceTopIcon;
036    @SideOnly(Side.CLIENT)
037    protected Icon furnaceFrontIcon;
038    @SideOnly(Side.CLIENT)
039    protected Icon field_96473_e;
040
041    protected BlockDispenser(int par1)
042    {
043        super(par1, Material.rock);
044        this.setCreativeTab(CreativeTabs.tabRedstone);
045    }
046
047    /**
048     * How many world ticks before ticking
049     */
050    public int tickRate(World par1World)
051    {
052        return 4;
053    }
054
055    /**
056     * Called whenever the block is added into the world. Args: world, x, y, z
057     */
058    public void onBlockAdded(World par1World, int par2, int par3, int par4)
059    {
060        super.onBlockAdded(par1World, par2, par3, par4);
061        this.setDispenserDefaultDirection(par1World, par2, par3, par4);
062    }
063
064    /**
065     * sets Dispenser block direction so that the front faces an non-opaque block; chooses west to be direction if all
066     * surrounding blocks are opaque.
067     */
068    private void setDispenserDefaultDirection(World par1World, int par2, int par3, int par4)
069    {
070        if (!par1World.isRemote)
071        {
072            int l = par1World.getBlockId(par2, par3, par4 - 1);
073            int i1 = par1World.getBlockId(par2, par3, par4 + 1);
074            int j1 = par1World.getBlockId(par2 - 1, par3, par4);
075            int k1 = par1World.getBlockId(par2 + 1, par3, par4);
076            byte b0 = 3;
077
078            if (Block.opaqueCubeLookup[l] && !Block.opaqueCubeLookup[i1])
079            {
080                b0 = 3;
081            }
082
083            if (Block.opaqueCubeLookup[i1] && !Block.opaqueCubeLookup[l])
084            {
085                b0 = 2;
086            }
087
088            if (Block.opaqueCubeLookup[j1] && !Block.opaqueCubeLookup[k1])
089            {
090                b0 = 5;
091            }
092
093            if (Block.opaqueCubeLookup[k1] && !Block.opaqueCubeLookup[j1])
094            {
095                b0 = 4;
096            }
097
098            par1World.setBlockMetadataWithNotify(par2, par3, par4, b0, 2);
099        }
100    }
101
102    @SideOnly(Side.CLIENT)
103
104    /**
105     * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
106     */
107    public Icon getIcon(int par1, int par2)
108    {
109        int k = par2 & 7;
110        return par1 == k ? (k != 1 && k != 0 ? this.furnaceFrontIcon : this.field_96473_e) : (k != 1 && k != 0 ? (par1 != 1 && par1 != 0 ? this.blockIcon : this.furnaceTopIcon) : this.furnaceTopIcon);
111    }
112
113    @SideOnly(Side.CLIENT)
114
115    /**
116     * When this method is called, your block should register all the icons it needs with the given IconRegister. This
117     * is the only chance you get to register icons.
118     */
119    public void registerIcons(IconRegister par1IconRegister)
120    {
121        this.blockIcon = par1IconRegister.registerIcon("furnace_side");
122        this.furnaceTopIcon = par1IconRegister.registerIcon("furnace_top");
123        this.furnaceFrontIcon = par1IconRegister.registerIcon("dispenser_front");
124        this.field_96473_e = par1IconRegister.registerIcon("dispenser_front_vertical");
125    }
126
127    /**
128     * Called upon block activation (right click on the block.)
129     */
130    public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
131    {
132        if (par1World.isRemote)
133        {
134            return true;
135        }
136        else
137        {
138            TileEntityDispenser tileentitydispenser = (TileEntityDispenser)par1World.getBlockTileEntity(par2, par3, par4);
139
140            if (tileentitydispenser != null)
141            {
142                par5EntityPlayer.displayGUIDispenser(tileentitydispenser);
143            }
144
145            return true;
146        }
147    }
148
149    protected void dispense(World par1World, int par2, int par3, int par4)
150    {
151        BlockSourceImpl blocksourceimpl = new BlockSourceImpl(par1World, par2, par3, par4);
152        TileEntityDispenser tileentitydispenser = (TileEntityDispenser)blocksourceimpl.getBlockTileEntity();
153
154        if (tileentitydispenser != null)
155        {
156            int l = tileentitydispenser.getRandomStackFromInventory();
157
158            if (l < 0)
159            {
160                par1World.playAuxSFX(1001, par2, par3, par4, 0);
161            }
162            else
163            {
164                ItemStack itemstack = tileentitydispenser.getStackInSlot(l);
165                IBehaviorDispenseItem ibehaviordispenseitem = this.getBehaviorForItemStack(itemstack);
166
167                if (ibehaviordispenseitem != IBehaviorDispenseItem.itemDispenseBehaviorProvider)
168                {
169                    ItemStack itemstack1 = ibehaviordispenseitem.dispense(blocksourceimpl, itemstack);
170                    tileentitydispenser.setInventorySlotContents(l, itemstack1.stackSize == 0 ? null : itemstack1);
171                }
172            }
173        }
174    }
175
176    /**
177     * Returns the behavior for the given ItemStack.
178     */
179    protected IBehaviorDispenseItem getBehaviorForItemStack(ItemStack par1ItemStack)
180    {
181        return (IBehaviorDispenseItem)dispenseBehaviorRegistry.func_82594_a(par1ItemStack.getItem());
182    }
183
184    /**
185     * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
186     * their own) Args: x, y, z, neighbor blockID
187     */
188    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
189    {
190        boolean flag = par1World.isBlockIndirectlyGettingPowered(par2, par3, par4) || par1World.isBlockIndirectlyGettingPowered(par2, par3 + 1, par4);
191        int i1 = par1World.getBlockMetadata(par2, par3, par4);
192        boolean flag1 = (i1 & 8) != 0;
193
194        if (flag && !flag1)
195        {
196            par1World.scheduleBlockUpdate(par2, par3, par4, this.blockID, this.tickRate(par1World));
197            par1World.setBlockMetadataWithNotify(par2, par3, par4, i1 | 8, 4);
198        }
199        else if (!flag && flag1)
200        {
201            par1World.setBlockMetadataWithNotify(par2, par3, par4, i1 & -9, 4);
202        }
203    }
204
205    /**
206     * Ticks the block if it's been scheduled
207     */
208    public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
209    {
210        if (!par1World.isRemote)
211        {
212            this.dispense(par1World, par2, par3, par4);
213        }
214    }
215
216    /**
217     * Returns a new instance of a block's tile entity class. Called on placing the block.
218     */
219    public TileEntity createNewTileEntity(World par1World)
220    {
221        return new TileEntityDispenser();
222    }
223
224    /**
225     * Called when the block is placed in the world.
226     */
227    public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving, ItemStack par6ItemStack)
228    {
229        int l = BlockPistonBase.determineOrientation(par1World, par2, par3, par4, par5EntityLiving);
230        par1World.setBlockMetadataWithNotify(par2, par3, par4, l, 2);
231
232        if (par6ItemStack.hasDisplayName())
233        {
234            ((TileEntityDispenser)par1World.getBlockTileEntity(par2, par3, par4)).func_94049_a(par6ItemStack.getDisplayName());
235        }
236    }
237
238    /**
239     * ejects contained items into the world, and notifies neighbours of an update, as appropriate
240     */
241    public void breakBlock(World par1World, int par2, int par3, int par4, int par5, int par6)
242    {
243        TileEntityDispenser tileentitydispenser = (TileEntityDispenser)par1World.getBlockTileEntity(par2, par3, par4);
244
245        if (tileentitydispenser != null)
246        {
247            for (int j1 = 0; j1 < tileentitydispenser.getSizeInventory(); ++j1)
248            {
249                ItemStack itemstack = tileentitydispenser.getStackInSlot(j1);
250
251                if (itemstack != null)
252                {
253                    float f = this.random.nextFloat() * 0.8F + 0.1F;
254                    float f1 = this.random.nextFloat() * 0.8F + 0.1F;
255                    float f2 = this.random.nextFloat() * 0.8F + 0.1F;
256
257                    while (itemstack.stackSize > 0)
258                    {
259                        int k1 = this.random.nextInt(21) + 10;
260
261                        if (k1 > itemstack.stackSize)
262                        {
263                            k1 = itemstack.stackSize;
264                        }
265
266                        itemstack.stackSize -= k1;
267                        EntityItem entityitem = new EntityItem(par1World, (double)((float)par2 + f), (double)((float)par3 + f1), (double)((float)par4 + f2), new ItemStack(itemstack.itemID, k1, itemstack.getItemDamage()));
268
269                        if (itemstack.hasTagCompound())
270                        {
271                            entityitem.getEntityItem().setTagCompound((NBTTagCompound)itemstack.getTagCompound().copy());
272                        }
273
274                        float f3 = 0.05F;
275                        entityitem.motionX = (double)((float)this.random.nextGaussian() * f3);
276                        entityitem.motionY = (double)((float)this.random.nextGaussian() * f3 + 0.2F);
277                        entityitem.motionZ = (double)((float)this.random.nextGaussian() * f3);
278                        par1World.spawnEntityInWorld(entityitem);
279                    }
280                }
281            }
282
283            par1World.func_96440_m(par2, par3, par4, par5);
284        }
285
286        super.breakBlock(par1World, par2, par3, par4, par5, par6);
287    }
288
289    public static IPosition getIPositionFromBlockSource(IBlockSource par0IBlockSource)
290    {
291        EnumFacing enumfacing = getFacing(par0IBlockSource.getBlockMetadata());
292        double d0 = par0IBlockSource.getX() + 0.7D * (double)enumfacing.getFrontOffsetX();
293        double d1 = par0IBlockSource.getY() + 0.7D * (double)enumfacing.getFrontOffsetY();
294        double d2 = par0IBlockSource.getZ() + 0.7D * (double)enumfacing.getFrontOffsetZ();
295        return new PositionImpl(d0, d1, d2);
296    }
297
298    public static EnumFacing getFacing(int par0)
299    {
300        return EnumFacing.getFront(par0 & 7);
301    }
302
303    /**
304     * If this returns true, then comparators facing away from this block will use the value from
305     * getComparatorInputOverride instead of the actual redstone signal strength.
306     */
307    public boolean hasComparatorInputOverride()
308    {
309        return true;
310    }
311
312    /**
313     * If hasComparatorInputOverride returns true, the return value from this is used instead of the redstone signal
314     * strength when this block inputs to a comparator.
315     */
316    public int getComparatorInputOverride(World par1World, int par2, int par3, int par4, int par5)
317    {
318        return Container.func_94526_b((IInventory)par1World.getBlockTileEntity(par2, par3, par4));
319    }
320}