001    package net.minecraft.item;
002    
003    import net.minecraft.block.Block;
004    import net.minecraft.entity.player.EntityPlayer;
005    import net.minecraft.entity.player.EntityPlayerMP;
006    import net.minecraft.network.packet.Packet53BlockChange;
007    import net.minecraft.world.EnumGameType;
008    import net.minecraft.world.World;
009    import net.minecraft.world.WorldServer;
010    
011    import net.minecraftforge.common.ForgeHooks;
012    import net.minecraftforge.common.MinecraftForge;
013    import net.minecraftforge.event.Event;
014    import net.minecraftforge.event.ForgeEventFactory;
015    import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
016    import net.minecraftforge.event.entity.player.PlayerInteractEvent;
017    import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action;
018    
019    public 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    }