001package net.minecraft.item;
002
003import net.minecraft.block.Block;
004import net.minecraft.entity.player.EntityPlayer;
005import net.minecraft.entity.player.EntityPlayerMP;
006import net.minecraft.network.packet.Packet53BlockChange;
007import net.minecraft.world.EnumGameType;
008import net.minecraft.world.World;
009import net.minecraft.world.WorldServer;
010
011import net.minecraftforge.common.ForgeHooks;
012import net.minecraftforge.common.MinecraftForge;
013import net.minecraftforge.event.Event;
014import net.minecraftforge.event.ForgeEventFactory;
015import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
016import net.minecraftforge.event.entity.player.PlayerInteractEvent;
017import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action;
018
019public class ItemInWorldManager
020{
021    /** Forge reach distance */
022    private double blockReachDistance = 5.0d;
023
024    /** The world object that this object is connected to. */
025    public World theWorld;
026
027    /** The EntityPlayerMP object that this object is connected to. */
028    public EntityPlayerMP thisPlayerMP;
029    private EnumGameType gameType;
030
031    /** True if the player is destroying a block */
032    private boolean isDestroyingBlock;
033    private int initialDamage;
034    private int partiallyDestroyedBlockX;
035    private int partiallyDestroyedBlockY;
036    private int partiallyDestroyedBlockZ;
037    private int curblockDamage;
038
039    /**
040     * Set to true when the "finished destroying block" packet is received but the block wasn't fully damaged yet. The
041     * block will not be destroyed while this is false.
042     */
043    private boolean receivedFinishDiggingPacket;
044    private int posX;
045    private int posY;
046    private int posZ;
047    private int field_73093_n;
048    private int durabilityRemainingOnBlock;
049
050    public ItemInWorldManager(World par1World)
051    {
052        this.gameType = EnumGameType.NOT_SET;
053        this.durabilityRemainingOnBlock = -1;
054        this.theWorld = par1World;
055    }
056
057    public void setGameType(EnumGameType par1EnumGameType)
058    {
059        this.gameType = par1EnumGameType;
060        par1EnumGameType.configurePlayerCapabilities(this.thisPlayerMP.capabilities);
061        this.thisPlayerMP.sendPlayerAbilities();
062    }
063
064    public EnumGameType getGameType()
065    {
066        return this.gameType;
067    }
068
069    /**
070     * Get if we are in creative game mode.
071     */
072    public boolean isCreative()
073    {
074        return this.gameType.isCreative();
075    }
076
077    /**
078     * if the gameType is currently NOT_SET then change it to par1
079     */
080    public void initializeGameType(EnumGameType par1EnumGameType)
081    {
082        if (this.gameType == EnumGameType.NOT_SET)
083        {
084            this.gameType = par1EnumGameType;
085        }
086
087        this.setGameType(this.gameType);
088    }
089
090    public void updateBlockRemoving()
091    {
092        ++this.curblockDamage;
093        int i;
094        float f;
095        int j;
096
097        if (this.receivedFinishDiggingPacket)
098        {
099            i = this.curblockDamage - this.field_73093_n;
100            int k = this.theWorld.getBlockId(this.posX, this.posY, this.posZ);
101
102            if (k == 0)
103            {
104                this.receivedFinishDiggingPacket = false;
105            }
106            else
107            {
108                Block block = Block.blocksList[k];
109                f = block.getPlayerRelativeBlockHardness(this.thisPlayerMP, this.thisPlayerMP.worldObj, this.posX, this.posY, this.posZ) * (float)(i + 1);
110                j = (int)(f * 10.0F);
111
112                if (j != this.durabilityRemainingOnBlock)
113                {
114                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, this.posX, this.posY, this.posZ, j);
115                    this.durabilityRemainingOnBlock = j;
116                }
117
118                if (f >= 1.0F)
119                {
120                    this.receivedFinishDiggingPacket = false;
121                    this.tryHarvestBlock(this.posX, this.posY, this.posZ);
122                }
123            }
124        }
125        else if (this.isDestroyingBlock)
126        {
127            i = this.theWorld.getBlockId(this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ);
128            Block block1 = Block.blocksList[i];
129
130            if (block1 == null)
131            {
132                this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ, -1);
133                this.durabilityRemainingOnBlock = -1;
134                this.isDestroyingBlock = false;
135            }
136            else
137            {
138                int l = this.curblockDamage - this.initialDamage;
139                f = block1.getPlayerRelativeBlockHardness(this.thisPlayerMP, this.thisPlayerMP.worldObj, this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ) * (float)(l + 1);
140                j = (int)(f * 10.0F);
141
142                if (j != this.durabilityRemainingOnBlock)
143                {
144                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ, j);
145                    this.durabilityRemainingOnBlock = j;
146                }
147            }
148        }
149    }
150
151    /**
152     * if not creative, it calls destroyBlockInWorldPartially untill the block is broken first. par4 is the specific
153     * side. tryHarvestBlock can also be the result of this call
154     */
155    public void onBlockClicked(int par1, int par2, int par3, int par4)
156    {
157        if (!this.gameType.isAdventure() || this.thisPlayerMP.canCurrentToolHarvestBlock(par1, par2, par3))
158        {
159            PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(thisPlayerMP, Action.LEFT_CLICK_BLOCK, par1, par2, par3, par4);
160            if (event.isCanceled())
161            {
162                thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par1, par2, par3, theWorld));
163                return;
164            }
165
166            if (this.isCreative())
167            {
168                if (!this.theWorld.extinguishFire((EntityPlayer)null, par1, par2, par3, par4))
169                {
170                    this.tryHarvestBlock(par1, par2, par3);
171                }
172            }
173            else
174            {
175                this.initialDamage = this.curblockDamage;
176                float f = 1.0F;
177                int i1 = this.theWorld.getBlockId(par1, par2, par3);
178
179                Block block = Block.blocksList[i1];
180
181                if (block != null)
182                {
183                    if (event.useBlock != Event.Result.DENY)
184                    {
185                        block.onBlockClicked(theWorld, par1, par2, par3, thisPlayerMP);
186                        theWorld.extinguishFire(thisPlayerMP, par1, par2, par3, par4);
187                    }
188                    else
189                    {
190                        thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par1, par2, par3, theWorld));
191                    }
192                    f = block.getPlayerRelativeBlockHardness(thisPlayerMP, thisPlayerMP.worldObj, par1, par2, par3);
193                }
194
195                if (event.useItem == Event.Result.DENY)
196                {
197                    if (f >= 1.0f)
198                    {
199                        thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par1, par2, par3, theWorld));
200                    }
201                    return;
202                }
203
204                if (i1 > 0 && f >= 1.0F)
205                {
206                    this.tryHarvestBlock(par1, par2, par3);
207                }
208                else
209                {
210                    this.isDestroyingBlock = true;
211                    this.partiallyDestroyedBlockX = par1;
212                    this.partiallyDestroyedBlockY = par2;
213                    this.partiallyDestroyedBlockZ = par3;
214                    int j1 = (int)(f * 10.0F);
215                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, par1, par2, par3, j1);
216                    this.durabilityRemainingOnBlock = j1;
217                }
218            }
219        }
220    }
221
222    public void uncheckedTryHarvestBlock(int par1, int par2, int par3)
223    {
224        if (par1 == this.partiallyDestroyedBlockX && par2 == this.partiallyDestroyedBlockY && par3 == this.partiallyDestroyedBlockZ)
225        {
226            int l = this.curblockDamage - this.initialDamage;
227            int i1 = this.theWorld.getBlockId(par1, par2, par3);
228
229            if (i1 != 0)
230            {
231                Block block = Block.blocksList[i1];
232                float f = block.getPlayerRelativeBlockHardness(this.thisPlayerMP, this.thisPlayerMP.worldObj, par1, par2, par3) * (float)(l + 1);
233
234                if (f >= 0.7F)
235                {
236                    this.isDestroyingBlock = false;
237                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, par1, par2, par3, -1);
238                    this.tryHarvestBlock(par1, par2, par3);
239                }
240                else if (!this.receivedFinishDiggingPacket)
241                {
242                    this.isDestroyingBlock = false;
243                    this.receivedFinishDiggingPacket = true;
244                    this.posX = par1;
245                    this.posY = par2;
246                    this.posZ = par3;
247                    this.field_73093_n = this.initialDamage;
248                }
249            }
250        }
251    }
252
253    /**
254     * note: this ignores the pars passed in and continues to destroy the onClickedBlock
255     */
256    public void cancelDestroyingBlock(int par1, int par2, int par3)
257    {
258        this.isDestroyingBlock = false;
259        this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ, -1);
260    }
261
262    /**
263     * Removes a block and triggers the appropriate events
264     */
265    private boolean removeBlock(int par1, int par2, int par3)
266    {
267        Block block = Block.blocksList[this.theWorld.getBlockId(par1, par2, par3)];
268        int l = this.theWorld.getBlockMetadata(par1, par2, par3);
269
270        if (block != null)
271        {
272            block.onBlockHarvested(this.theWorld, par1, par2, par3, l, this.thisPlayerMP);
273        }
274
275        boolean flag = (block != null && block.removeBlockByPlayer(theWorld, thisPlayerMP, par1, par2, par3));
276
277        if (block != null && flag)
278        {
279            block.onBlockDestroyedByPlayer(this.theWorld, par1, par2, par3, l);
280        }
281
282        return flag;
283    }
284
285    /**
286     * Attempts to harvest a block at the given coordinate
287     */
288    public boolean tryHarvestBlock(int par1, int par2, int par3)
289    {
290        if (this.gameType.isAdventure() && !this.thisPlayerMP.canCurrentToolHarvestBlock(par1, par2, par3))
291        {
292            return false;
293        }
294        else
295        {
296            ItemStack stack = thisPlayerMP.getCurrentEquippedItem();
297            if (stack != null && stack.getItem().onBlockStartBreak(stack, par1, par2, par3, thisPlayerMP))
298            {
299                return false;
300            }
301            int l = this.theWorld.getBlockId(par1, par2, par3);
302            int i1 = this.theWorld.getBlockMetadata(par1, par2, par3);
303            this.theWorld.playAuxSFXAtEntity(this.thisPlayerMP, 2001, par1, par2, par3, l + (this.theWorld.getBlockMetadata(par1, par2, par3) << 12));
304            boolean flag = false;
305
306            if (this.isCreative())
307            {
308                flag = this.removeBlock(par1, par2, par3);
309                this.thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par1, par2, par3, this.theWorld));
310            }
311            else
312            {
313                ItemStack itemstack = this.thisPlayerMP.getCurrentEquippedItem();
314                boolean flag1 = false;
315                Block block = Block.blocksList[l];
316                if (block != null)
317                {
318                    flag1 = block.canHarvestBlock(thisPlayerMP, i1);
319                }
320
321                if (itemstack != null)
322                {
323                    itemstack.onBlockDestroyed(this.theWorld, l, par1, par2, par3, this.thisPlayerMP);
324
325                    if (itemstack.stackSize == 0)
326                    {
327                        this.thisPlayerMP.destroyCurrentEquippedItem();
328                    }
329                }
330
331                flag = this.removeBlock(par1, par2, par3);
332                if (flag && flag1)
333                {
334                    Block.blocksList[l].harvestBlock(this.theWorld, this.thisPlayerMP, par1, par2, par3, i1);
335                }
336            }
337
338            return flag;
339        }
340    }
341
342    /**
343     * Attempts to right-click use an item by the given EntityPlayer in the given World
344     */
345    public boolean tryUseItem(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack)
346    {
347        int i = par3ItemStack.stackSize;
348        int j = par3ItemStack.getItemDamage();
349        ItemStack itemstack1 = par3ItemStack.useItemRightClick(par2World, par1EntityPlayer);
350
351        if (itemstack1 == par3ItemStack && (itemstack1 == null || itemstack1.stackSize == i && itemstack1.getMaxItemUseDuration() <= 0 && itemstack1.getItemDamage() == j))
352        {
353            return false;
354        }
355        else
356        {
357            par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = itemstack1;
358
359            if (this.isCreative())
360            {
361                itemstack1.stackSize = i;
362
363                if (itemstack1.isItemStackDamageable())
364                {
365                    itemstack1.setItemDamage(j);
366                }
367            }
368
369            if (itemstack1.stackSize == 0)
370            {
371                par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = null;
372                MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(thisPlayerMP, itemstack1));
373            }
374
375            if (!par1EntityPlayer.isUsingItem())
376            {
377                ((EntityPlayerMP)par1EntityPlayer).sendContainerToPlayer(par1EntityPlayer.inventoryContainer);
378            }
379
380            return true;
381        }
382    }
383
384    /**
385     * Activate the clicked on block, otherwise use the held item. Args: player, world, itemStack, x, y, z, side,
386     * xOffset, yOffset, zOffset
387     */
388    public boolean activateBlockOrUseItem(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack, int par4, int par5, int par6, int par7, float par8, float par9, float par10)
389    {
390        PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(par1EntityPlayer, Action.RIGHT_CLICK_BLOCK, par4, par5, par6, par7);
391        if (event.isCanceled())
392        {
393            thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par4, par5, par6, theWorld));
394            return false;
395        }
396
397        Item item = (par3ItemStack != null ? par3ItemStack.getItem() : null);
398        if (item != null && item.onItemUseFirst(par3ItemStack, par1EntityPlayer, par2World, par4, par5, par6, par7, par8, par9, par10))
399        {
400            if (par3ItemStack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(thisPlayerMP, par3ItemStack);
401            return true;
402        }
403
404        int i1 = par2World.getBlockId(par4, par5, par6);
405        Block block = Block.blocksList[i1];
406        boolean result = false;
407
408        if (block != null && (!par1EntityPlayer.isSneaking() || ( par1EntityPlayer.getHeldItem() == null || par1EntityPlayer.getHeldItem().getItem().shouldPassSneakingClickToBlock(par2World, par4, par5, par6))))
409        {
410            if (event.useBlock != Event.Result.DENY)
411            {
412                result = block.onBlockActivated(par2World, par4, par5, par6, par1EntityPlayer, par7, par8, par9, par10);
413            }
414            else
415            {
416                thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par4, par5, par6, theWorld));
417                result = event.useItem != Event.Result.ALLOW;
418            }
419        }
420
421        if (par3ItemStack != null && !result && event.useItem != Event.Result.DENY)
422        {
423            int meta = par3ItemStack.getItemDamage();
424            int size = par3ItemStack.stackSize;
425            result = par3ItemStack.tryPlaceItemIntoWorld(par1EntityPlayer, par2World, par4, par5, par6, par7, par8, par9, par10);
426            if (isCreative())
427            {
428                par3ItemStack.setItemDamage(meta);
429                par3ItemStack.stackSize = size;
430            }
431            if (par3ItemStack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(thisPlayerMP, par3ItemStack);
432        }
433
434        /* Re-enable if this causes bukkit incompatibility, or re-write client side to only send a single packet per right click.
435        if (par3ItemStack != null && ((!result && event.useItem != Event.Result.DENY) || event.useItem == Event.Result.ALLOW))
436        {
437            this.tryUseItem(thisPlayerMP, par2World, par3ItemStack);
438        }*/
439        return result;
440    }
441
442    /**
443     * Sets the world instance.
444     */
445    public void setWorld(WorldServer par1WorldServer)
446    {
447        this.theWorld = par1WorldServer;
448    }
449
450    public double getBlockReachDistance()
451    {
452        return blockReachDistance;
453    }
454    public void setBlockReachDistance(double distance)
455    {
456        blockReachDistance = distance;
457    }
458}