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 var1;
094        float var4;
095        int var5;
096
097        if (this.receivedFinishDiggingPacket)
098        {
099            var1 = this.curblockDamage - this.field_73093_n;
100            int var2 = this.theWorld.getBlockId(this.posX, this.posY, this.posZ);
101
102            if (var2 == 0)
103            {
104                this.receivedFinishDiggingPacket = false;
105            }
106            else
107            {
108                Block var3 = Block.blocksList[var2];
109                var4 = var3.getPlayerRelativeBlockHardness(this.thisPlayerMP, this.thisPlayerMP.worldObj, this.posX, this.posY, this.posZ) * (float)(var1 + 1);
110                var5 = (int)(var4 * 10.0F);
111
112                if (var5 != this.durabilityRemainingOnBlock)
113                {
114                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, this.posX, this.posY, this.posZ, var5);
115                    this.durabilityRemainingOnBlock = var5;
116                }
117
118                if (var4 >= 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            var1 = this.theWorld.getBlockId(this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ);
128            Block var6 = Block.blocksList[var1];
129
130            if (var6 == 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 var7 = this.curblockDamage - this.initialDamage;
139                var4 = var6.getPlayerRelativeBlockHardness(this.thisPlayerMP, this.thisPlayerMP.worldObj, this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ) * (float)(var7 + 1);
140                var5 = (int)(var4 * 10.0F);
141
142                if (var5 != this.durabilityRemainingOnBlock)
143                {
144                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ, var5);
145                    this.durabilityRemainingOnBlock = var5;
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 var5 = 1.0F;
177                int var6 = this.theWorld.getBlockId(par1, par2, par3);
178                Block block = Block.blocksList[var6];
179
180                if (block != null)
181                {
182                    if (event.useBlock != Event.Result.DENY)
183                    {
184                        block.onBlockClicked(theWorld, par1, par2, par3, thisPlayerMP);
185                        theWorld.extinguishFire(thisPlayerMP, par1, par2, par3, par4);
186                    }
187                    else
188                    {
189                        thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par1, par2, par3, theWorld));
190                    }
191                    var5 = block.getPlayerRelativeBlockHardness(thisPlayerMP, thisPlayerMP.worldObj, par1, par2, par3);
192                }
193
194                if (event.useItem == Event.Result.DENY)
195                {
196                    if (var5 >= 1.0f)
197                    {
198                        thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par1, par2, par3, theWorld));
199                    }
200                    return;
201                }
202
203                if (var6 > 0 && var5 >= 1.0F)
204                {
205                    this.tryHarvestBlock(par1, par2, par3);
206                }
207                else
208                {
209                    this.isDestroyingBlock = true;
210                    this.partiallyDestroyedBlockX = par1;
211                    this.partiallyDestroyedBlockY = par2;
212                    this.partiallyDestroyedBlockZ = par3;
213                    int var7 = (int)(var5 * 10.0F);
214                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, par1, par2, par3, var7);
215                    this.durabilityRemainingOnBlock = var7;
216                }
217            }
218        }
219    }
220
221    public void uncheckedTryHarvestBlock(int par1, int par2, int par3)
222    {
223        if (par1 == this.partiallyDestroyedBlockX && par2 == this.partiallyDestroyedBlockY && par3 == this.partiallyDestroyedBlockZ)
224        {
225            int var4 = this.curblockDamage - this.initialDamage;
226            int var5 = this.theWorld.getBlockId(par1, par2, par3);
227
228            if (var5 != 0)
229            {
230                Block var6 = Block.blocksList[var5];
231                float var7 = var6.getPlayerRelativeBlockHardness(this.thisPlayerMP, this.thisPlayerMP.worldObj, par1, par2, par3) * (float)(var4 + 1);
232
233                if (var7 >= 0.7F)
234                {
235                    this.isDestroyingBlock = false;
236                    this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, par1, par2, par3, -1);
237                    this.tryHarvestBlock(par1, par2, par3);
238                }
239                else if (!this.receivedFinishDiggingPacket)
240                {
241                    this.isDestroyingBlock = false;
242                    this.receivedFinishDiggingPacket = true;
243                    this.posX = par1;
244                    this.posY = par2;
245                    this.posZ = par3;
246                    this.field_73093_n = this.initialDamage;
247                }
248            }
249        }
250    }
251
252    /**
253     * note: this ignores the pars passed in and continues to destroy the onClickedBlock
254     */
255    public void cancelDestroyingBlock(int par1, int par2, int par3)
256    {
257        this.isDestroyingBlock = false;
258        this.theWorld.destroyBlockInWorldPartially(this.thisPlayerMP.entityId, this.partiallyDestroyedBlockX, this.partiallyDestroyedBlockY, this.partiallyDestroyedBlockZ, -1);
259    }
260
261    /**
262     * Removes a block and triggers the appropriate events
263     */
264    private boolean removeBlock(int par1, int par2, int par3)
265    {
266        Block var4 = Block.blocksList[this.theWorld.getBlockId(par1, par2, par3)];
267        int var5 = this.theWorld.getBlockMetadata(par1, par2, par3);
268
269        if (var4 != null)
270        {
271            var4.onBlockHarvested(this.theWorld, par1, par2, par3, var5, this.thisPlayerMP);
272        }
273
274        boolean var6 = (var4 != null && var4.removeBlockByPlayer(theWorld, thisPlayerMP, par1, par2, par3));
275
276        if (var4 != null && var6)
277        {
278            var4.onBlockDestroyedByPlayer(this.theWorld, par1, par2, par3, var5);
279        }
280
281        return var6;
282    }
283
284    /**
285     * Attempts to harvest a block at the given coordinate
286     */
287    public boolean tryHarvestBlock(int par1, int par2, int par3)
288    {
289        if (this.gameType.isAdventure() && !this.thisPlayerMP.canCurrentToolHarvestBlock(par1, par2, par3))
290        {
291            return false;
292        }
293        else
294        {
295            ItemStack stack = thisPlayerMP.getCurrentEquippedItem();
296            if (stack != null && stack.getItem().onBlockStartBreak(stack, par1, par2, par3, thisPlayerMP))
297            {
298                return false;
299            }
300            int var4 = this.theWorld.getBlockId(par1, par2, par3);
301            int var5 = this.theWorld.getBlockMetadata(par1, par2, par3);
302            this.theWorld.playAuxSFXAtEntity(this.thisPlayerMP, 2001, par1, par2, par3, var4 + (this.theWorld.getBlockMetadata(par1, par2, par3) << 12));
303            boolean var6 = false;
304
305            if (this.isCreative())
306            {
307                var6 = this.removeBlock(par1, par2, par3);
308                this.thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par1, par2, par3, this.theWorld));
309            }
310            else
311            {
312                ItemStack var7 = this.thisPlayerMP.getCurrentEquippedItem();
313                boolean var8 = false;
314                Block block = Block.blocksList[var4];
315                if (block != null)
316                {
317                    var8 = block.canHarvestBlock(thisPlayerMP, var5);
318                }
319
320                if (var7 != null)
321                {
322                    var7.onBlockDestroyed(this.theWorld, var4, par1, par2, par3, this.thisPlayerMP);
323
324                    if (var7.stackSize == 0)
325                    {
326                        this.thisPlayerMP.destroyCurrentEquippedItem();
327                    }
328                }
329
330                var6 = this.removeBlock(par1, par2, par3);
331                if (var6 && var8)
332                {
333                    Block.blocksList[var4].harvestBlock(this.theWorld, this.thisPlayerMP, par1, par2, par3, var5);
334                }
335            }
336
337            return var6;
338        }
339    }
340
341    /**
342     * Attempts to right-click use an item by the given EntityPlayer in the given World
343     */
344    public boolean tryUseItem(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack)
345    {
346        int var4 = par3ItemStack.stackSize;
347        int var5 = par3ItemStack.getItemDamage();
348        ItemStack var6 = par3ItemStack.useItemRightClick(par2World, par1EntityPlayer);
349
350        if (var6 == par3ItemStack && (var6 == null || var6.stackSize == var4 && var6.getMaxItemUseDuration() <= 0 && var6.getItemDamage() == var5))
351        {
352            return false;
353        }
354        else
355        {
356            par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = var6;
357
358            if (this.isCreative())
359            {
360                var6.stackSize = var4;
361
362                if (var6.isItemStackDamageable())
363                {
364                    var6.setItemDamage(var5);
365                }
366            }
367
368            if (var6.stackSize == 0)
369            {
370                par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = null;
371                MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(thisPlayerMP, var6));
372            }
373
374            if (!par1EntityPlayer.isUsingItem())
375            {
376                ((EntityPlayerMP)par1EntityPlayer).sendContainerToPlayer(par1EntityPlayer.inventoryContainer);
377            }
378
379            return true;
380        }
381    }
382
383    /**
384     * Activate the clicked on block, otherwise use the held item. Args: player, world, itemStack, x, y, z, side,
385     * xOffset, yOffset, zOffset
386     */
387    public boolean activateBlockOrUseItem(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack, int par4, int par5, int par6, int par7, float par8, float par9, float par10)
388    {
389        PlayerInteractEvent event = ForgeEventFactory.onPlayerInteract(par1EntityPlayer, Action.RIGHT_CLICK_BLOCK, par4, par5, par6, par7);
390        if (event.isCanceled())
391        {
392            thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par4, par5, par6, theWorld));
393            return false;
394        }
395
396        Item item = (par3ItemStack != null ? par3ItemStack.getItem() : null);
397        if (item != null && item.onItemUseFirst(par3ItemStack, par1EntityPlayer, par2World, par4, par5, par6, par7, par8, par9, par10))
398        {
399            if (par3ItemStack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(thisPlayerMP, par3ItemStack);
400            return true;
401        }
402
403        int var11 = par2World.getBlockId(par4, par5, par6);
404        Block block = Block.blocksList[var11];
405        boolean result = false;
406
407        if (block != null && (!par1EntityPlayer.isSneaking() || ( par1EntityPlayer.getHeldItem() == null || par1EntityPlayer.getHeldItem().getItem().shouldPassSneakingClickToBlock(par2World, par4, par5, par6))))
408        {
409            if (event.useBlock != Event.Result.DENY)
410            {
411                result = block.onBlockActivated(par2World, par4, par5, par6, par1EntityPlayer, par7, par8, par9, par10);
412            }
413            else
414            {
415                thisPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet53BlockChange(par4, par5, par6, theWorld));
416                result = event.useItem != Event.Result.ALLOW;
417            }
418        }
419
420        if (par3ItemStack != null && !result && event.useItem != Event.Result.DENY)
421        {
422            int meta = par3ItemStack.getItemDamage();
423            int size = par3ItemStack.stackSize;
424            result = par3ItemStack.tryPlaceItemIntoWorld(par1EntityPlayer, par2World, par4, par5, par6, par7, par8, par9, par10);
425            if (isCreative())
426            {
427                par3ItemStack.setItemDamage(meta);
428                par3ItemStack.stackSize = size;
429            }
430            if (par3ItemStack.stackSize <= 0) ForgeEventFactory.onPlayerDestroyItem(thisPlayerMP, par3ItemStack);
431        }
432
433        /* Re-enable if this causes bukkit incompatibility, or re-write client side to only send a single packet per right click.
434        if (par3ItemStack != null && ((!result && event.useItem != Event.Result.DENY) || event.useItem == Event.Result.ALLOW))
435        {
436            this.tryUseItem(thisPlayerMP, par2World, par3ItemStack);
437        }*/
438        return result;
439    }
440
441    /**
442     * Sets the world instance.
443     */
444    public void setWorld(WorldServer par1WorldServer)
445    {
446        this.theWorld = par1WorldServer;
447    }
448
449    public double getBlockReachDistance()
450    {
451        return blockReachDistance;
452    }
453    public void setBlockReachDistance(double distance)
454    {
455        blockReachDistance = distance;
456    }
457}