001package net.minecraft.block;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import java.util.Iterator;
006import java.util.Random;
007import net.minecraft.block.material.Material;
008import net.minecraft.entity.Entity;
009import net.minecraft.entity.player.EntityPlayer;
010import net.minecraft.entity.player.EnumStatus;
011import net.minecraft.item.Item;
012import net.minecraft.util.ChunkCoordinates;
013import net.minecraft.util.Direction;
014import net.minecraft.world.IBlockAccess;
015import net.minecraft.world.World;
016
017public class BlockBed extends BlockDirectional
018{
019    /** Maps the foot-of-bed block to the head-of-bed block. */
020    public static final int[][] footBlockToHeadBlockMap = new int[][] {{0, 1}, { -1, 0}, {0, -1}, {1, 0}};
021
022    public BlockBed(int par1)
023    {
024        super(par1, 134, Material.cloth);
025        this.setBounds();
026    }
027
028    /**
029     * Called upon block activation (right click on the block.)
030     */
031    public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
032    {
033        if (par1World.isRemote)
034        {
035            return true;
036        }
037        else
038        {
039            int var10 = par1World.getBlockMetadata(par2, par3, par4);
040
041            if (!isBlockHeadOfBed(var10))
042            {
043                int var11 = getDirection(var10);
044                par2 += footBlockToHeadBlockMap[var11][0];
045                par4 += footBlockToHeadBlockMap[var11][1];
046
047                if (par1World.getBlockId(par2, par3, par4) != this.blockID)
048                {
049                    return true;
050                }
051
052                var10 = par1World.getBlockMetadata(par2, par3, par4);
053            }
054
055            if (!par1World.provider.canRespawnHere())
056            {
057                double var19 = (double)par2 + 0.5D;
058                double var21 = (double)par3 + 0.5D;
059                double var15 = (double)par4 + 0.5D;
060                par1World.setBlockWithNotify(par2, par3, par4, 0);
061                int var17 = getDirection(var10);
062                par2 += footBlockToHeadBlockMap[var17][0];
063                par4 += footBlockToHeadBlockMap[var17][1];
064
065                if (par1World.getBlockId(par2, par3, par4) == this.blockID)
066                {
067                    par1World.setBlockWithNotify(par2, par3, par4, 0);
068                    var19 = (var19 + (double)par2 + 0.5D) / 2.0D;
069                    var21 = (var21 + (double)par3 + 0.5D) / 2.0D;
070                    var15 = (var15 + (double)par4 + 0.5D) / 2.0D;
071                }
072
073                par1World.newExplosion((Entity)null, (double)((float)par2 + 0.5F), (double)((float)par3 + 0.5F), (double)((float)par4 + 0.5F), 5.0F, true, true);
074                return true;
075            }
076            else
077            {
078                if (isBedOccupied(var10))
079                {
080                    EntityPlayer var18 = null;
081                    Iterator var12 = par1World.playerEntities.iterator();
082
083                    while (var12.hasNext())
084                    {
085                        EntityPlayer var13 = (EntityPlayer)var12.next();
086
087                        if (var13.isPlayerSleeping())
088                        {
089                            ChunkCoordinates var14 = var13.playerLocation;
090
091                            if (var14.posX == par2 && var14.posY == par3 && var14.posZ == par4)
092                            {
093                                var18 = var13;
094                            }
095                        }
096                    }
097
098                    if (var18 != null)
099                    {
100                        par5EntityPlayer.addChatMessage("tile.bed.occupied");
101                        return true;
102                    }
103
104                    setBedOccupied(par1World, par2, par3, par4, false);
105                }
106
107                EnumStatus var20 = par5EntityPlayer.sleepInBedAt(par2, par3, par4);
108
109                if (var20 == EnumStatus.OK)
110                {
111                    setBedOccupied(par1World, par2, par3, par4, true);
112                    return true;
113                }
114                else
115                {
116                    if (var20 == EnumStatus.NOT_POSSIBLE_NOW)
117                    {
118                        par5EntityPlayer.addChatMessage("tile.bed.noSleep");
119                    }
120                    else if (var20 == EnumStatus.NOT_SAFE)
121                    {
122                        par5EntityPlayer.addChatMessage("tile.bed.notSafe");
123                    }
124
125                    return true;
126                }
127            }
128        }
129    }
130
131    /**
132     * From the specified side and block metadata retrieves the blocks texture. Args: side, metadata
133     */
134    public int getBlockTextureFromSideAndMetadata(int par1, int par2)
135    {
136        if (par1 == 0)
137        {
138            return Block.planks.blockIndexInTexture;
139        }
140        else
141        {
142            int var3 = getDirection(par2);
143            int var4 = Direction.bedDirection[var3][par1];
144            return isBlockHeadOfBed(par2) ? (var4 == 2 ? this.blockIndexInTexture + 2 + 16 : (var4 != 5 && var4 != 4 ? this.blockIndexInTexture + 1 : this.blockIndexInTexture + 1 + 16)) : (var4 == 3 ? this.blockIndexInTexture - 1 + 16 : (var4 != 5 && var4 != 4 ? this.blockIndexInTexture : this.blockIndexInTexture + 16));
145        }
146    }
147
148    /**
149     * The type of render function that is called for this block
150     */
151    public int getRenderType()
152    {
153        return 14;
154    }
155
156    /**
157     * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
158     */
159    public boolean renderAsNormalBlock()
160    {
161        return false;
162    }
163
164    /**
165     * Is this block (a) opaque and (b) a full 1m cube?  This determines whether or not to render the shared face of two
166     * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
167     */
168    public boolean isOpaqueCube()
169    {
170        return false;
171    }
172
173    /**
174     * Updates the blocks bounds based on its current state. Args: world, x, y, z
175     */
176    public void setBlockBoundsBasedOnState(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
177    {
178        this.setBounds();
179    }
180
181    /**
182     * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
183     * their own) Args: x, y, z, neighbor blockID
184     */
185    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
186    {
187        int var6 = par1World.getBlockMetadata(par2, par3, par4);
188        int var7 = getDirection(var6);
189
190        if (isBlockHeadOfBed(var6))
191        {
192            if (par1World.getBlockId(par2 - footBlockToHeadBlockMap[var7][0], par3, par4 - footBlockToHeadBlockMap[var7][1]) != this.blockID)
193            {
194                par1World.setBlockWithNotify(par2, par3, par4, 0);
195            }
196        }
197        else if (par1World.getBlockId(par2 + footBlockToHeadBlockMap[var7][0], par3, par4 + footBlockToHeadBlockMap[var7][1]) != this.blockID)
198        {
199            par1World.setBlockWithNotify(par2, par3, par4, 0);
200
201            if (!par1World.isRemote)
202            {
203                this.dropBlockAsItem(par1World, par2, par3, par4, var6, 0);
204            }
205        }
206    }
207
208    /**
209     * Returns the ID of the items to drop on destruction.
210     */
211    public int idDropped(int par1, Random par2Random, int par3)
212    {
213        return isBlockHeadOfBed(par1) ? 0 : Item.bed.itemID;
214    }
215
216    /**
217     * Set the bounds of the bed block.
218     */
219    private void setBounds()
220    {
221        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.5625F, 1.0F);
222    }
223
224    /**
225     * Returns whether or not this bed block is the head of the bed.
226     */
227    public static boolean isBlockHeadOfBed(int par0)
228    {
229        return (par0 & 8) != 0;
230    }
231
232    /**
233     * Return whether or not the bed is occupied.
234     */
235    public static boolean isBedOccupied(int par0)
236    {
237        return (par0 & 4) != 0;
238    }
239
240    /**
241     * Sets whether or not the bed is occupied.
242     */
243    public static void setBedOccupied(World par0World, int par1, int par2, int par3, boolean par4)
244    {
245        int var5 = par0World.getBlockMetadata(par1, par2, par3);
246
247        if (par4)
248        {
249            var5 |= 4;
250        }
251        else
252        {
253            var5 &= -5;
254        }
255
256        par0World.setBlockMetadataWithNotify(par1, par2, par3, var5);
257    }
258
259    /**
260     * Gets the nearest empty chunk coordinates for the player to wake up from a bed into.
261     */
262    public static ChunkCoordinates getNearestEmptyChunkCoordinates(World par0World, int par1, int par2, int par3, int par4)
263    {
264        int var5 = par0World.getBlockMetadata(par1, par2, par3);
265        int var6 = BlockDirectional.getDirection(var5);
266
267        for (int var7 = 0; var7 <= 1; ++var7)
268        {
269            int var8 = par1 - footBlockToHeadBlockMap[var6][0] * var7 - 1;
270            int var9 = par3 - footBlockToHeadBlockMap[var6][1] * var7 - 1;
271            int var10 = var8 + 2;
272            int var11 = var9 + 2;
273
274            for (int var12 = var8; var12 <= var10; ++var12)
275            {
276                for (int var13 = var9; var13 <= var11; ++var13)
277                {
278                    if (par0World.doesBlockHaveSolidTopSurface(var12, par2 - 1, var13) && par0World.isAirBlock(var12, par2, var13) && par0World.isAirBlock(var12, par2 + 1, var13))
279                    {
280                        if (par4 <= 0)
281                        {
282                            return new ChunkCoordinates(var12, par2, var13);
283                        }
284
285                        --par4;
286                    }
287                }
288            }
289        }
290
291        return null;
292    }
293
294    /**
295     * Drops the block items with a specified chance of dropping the specified items
296     */
297    public void dropBlockAsItemWithChance(World par1World, int par2, int par3, int par4, int par5, float par6, int par7)
298    {
299        if (!isBlockHeadOfBed(par5))
300        {
301            super.dropBlockAsItemWithChance(par1World, par2, par3, par4, par5, par6, 0);
302        }
303    }
304
305    /**
306     * Returns the mobility information of the block, 0 = free, 1 = can't push but can move over, 2 = total immobility
307     * and stop pistons
308     */
309    public int getMobilityFlag()
310    {
311        return 1;
312    }
313
314    @SideOnly(Side.CLIENT)
315
316    /**
317     * only called by clickMiddleMouseButton , and passed to inventory.setCurrentItem (along with isCreative)
318     */
319    public int idPicked(World par1World, int par2, int par3, int par4)
320    {
321        return Item.bed.itemID;
322    }
323
324    /**
325     * Called when the block is attempted to be harvested
326     */
327    public void onBlockHarvested(World par1World, int par2, int par3, int par4, int par5, EntityPlayer par6EntityPlayer)
328    {
329        if (par6EntityPlayer.capabilities.isCreativeMode && isBlockHeadOfBed(par5))
330        {
331            int var7 = getDirection(par5);
332            par2 -= footBlockToHeadBlockMap[var7][0];
333            par4 -= footBlockToHeadBlockMap[var7][1];
334
335            if (par1World.getBlockId(par2, par3, par4) == this.blockID)
336            {
337                par1World.setBlockWithNotify(par2, par3, par4, 0);
338            }
339        }
340    }
341}