001    package net.minecraft.src;
002    
003    import cpw.mods.fml.common.Side;
004    import cpw.mods.fml.common.asm.SideOnly;
005    import net.minecraft.client.Minecraft;
006    import net.minecraftforge.common.ForgeHooks;
007    import net.minecraftforge.common.MinecraftForge;
008    import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
009    
010    @SideOnly(Side.CLIENT)
011    public class PlayerControllerMP
012    {
013        /** The Minecraft instance. */
014        private final Minecraft mc;
015        private final NetClientHandler netClientHandler;
016    
017        /** PosX of the current block being destroyed */
018        private int currentBlockX = -1;
019    
020        /** PosY of the current block being destroyed */
021        private int currentBlockY = -1;
022    
023        /** PosZ of the current block being destroyed */
024        private int currentblockZ = -1;
025        private ItemStack field_85183_f = null;
026    
027        /** Current block damage (MP) */
028        private float curBlockDamageMP = 0.0F;
029    
030        /** Previous block damage (MP) */
031        private float prevBlockDamageMP = 0.0F;
032    
033        /**
034         * Tick counter, when it hits 4 it resets back to 0 and plays the step sound
035         */
036        private float stepSoundTickCounter = 0.0F;
037    
038        /**
039         * Delays the first damage on the block after the first click on the block
040         */
041        private int blockHitDelay = 0;
042    
043        /** Tells if the player is hitting a block */
044        private boolean isHittingBlock = false;
045    
046        /** Current game type for the player */
047        private EnumGameType currentGameType;
048    
049        /** Index of the current item held by the player in the inventory hotbar */
050        private int currentPlayerItem;
051    
052        public PlayerControllerMP(Minecraft par1Minecraft, NetClientHandler par2NetClientHandler)
053        {
054            this.currentGameType = EnumGameType.SURVIVAL;
055            this.currentPlayerItem = 0;
056            this.mc = par1Minecraft;
057            this.netClientHandler = par2NetClientHandler;
058        }
059    
060        /**
061         * Block dig operation in creative mode (instantly digs the block).
062         */
063        public static void clickBlockCreative(Minecraft par0Minecraft, PlayerControllerMP par1PlayerControllerMP, int par2, int par3, int par4, int par5)
064        {
065            if (!par0Minecraft.theWorld.extinguishFire(par0Minecraft.thePlayer, par2, par3, par4, par5))
066            {
067                par1PlayerControllerMP.onPlayerDestroyBlock(par2, par3, par4, par5);
068            }
069        }
070    
071        /**
072         * Sets player capabilities depending on current gametype. params: player
073         */
074        public void setPlayerCapabilities(EntityPlayer par1EntityPlayer)
075        {
076            this.currentGameType.configurePlayerCapabilities(par1EntityPlayer.capabilities);
077        }
078    
079        public boolean func_78747_a()
080        {
081            return false;
082        }
083    
084        /**
085         * Sets the game type for the player.
086         */
087        public void setGameType(EnumGameType par1EnumGameType)
088        {
089            this.currentGameType = par1EnumGameType;
090            this.currentGameType.configurePlayerCapabilities(this.mc.thePlayer.capabilities);
091        }
092    
093        /**
094         * Flips the player around. Args: player
095         */
096        public void flipPlayer(EntityPlayer par1EntityPlayer)
097        {
098            par1EntityPlayer.rotationYaw = -180.0F;
099        }
100    
101        public boolean shouldDrawHUD()
102        {
103            return this.currentGameType.isSurvivalOrAdventure();
104        }
105    
106        /**
107         * Called when a player completes the destruction of a block
108         */
109        public boolean onPlayerDestroyBlock(int par1, int par2, int par3, int par4)
110        {
111            ItemStack stack = mc.thePlayer.getCurrentEquippedItem();
112            if (stack != null && stack.getItem() != null && stack.getItem().onBlockStartBreak(stack, par1, par2, par3, mc.thePlayer))
113            {
114                return false;
115            }
116    
117            if (this.currentGameType.isAdventure() && !this.mc.thePlayer.canCurrentToolHarvestBlock(par1, par2, par3))
118            {
119                return false;
120            }
121            else
122            {
123                WorldClient var5 = this.mc.theWorld;
124                Block var6 = Block.blocksList[var5.getBlockId(par1, par2, par3)];
125    
126                if (var6 == null)
127                {
128                    return false;
129                }
130                else
131                {
132                    var5.playAuxSFX(2001, par1, par2, par3, var6.blockID + (var5.getBlockMetadata(par1, par2, par3) << 12));
133                    int var7 = var5.getBlockMetadata(par1, par2, par3);
134                    boolean var8 = var6.removeBlockByPlayer(var5, mc.thePlayer, par1, par2, par3);
135    
136                    if (var8)
137                    {
138                        var6.onBlockDestroyedByPlayer(var5, par1, par2, par3, var7);
139                    }
140    
141                    this.currentBlockY = -1;
142    
143                    if (!this.currentGameType.isCreative())
144                    {
145                        ItemStack var9 = this.mc.thePlayer.getCurrentEquippedItem();
146    
147                        if (var9 != null)
148                        {
149                            var9.onBlockDestroyed(var5, var6.blockID, par1, par2, par3, this.mc.thePlayer);
150    
151                            if (var9.stackSize == 0)
152                            {
153                                this.mc.thePlayer.destroyCurrentEquippedItem();
154                            }
155                        }
156                    }
157    
158                    return var8;
159                }
160            }
161        }
162    
163        /**
164         * Called by Minecraft class when the player is hitting a block with an item. Args: x, y, z, side
165         */
166        public void clickBlock(int par1, int par2, int par3, int par4)
167        {
168            if (!this.currentGameType.isAdventure() || this.mc.thePlayer.canCurrentToolHarvestBlock(par1, par2, par3))
169            {
170                if (this.currentGameType.isCreative())
171                {
172                    this.netClientHandler.addToSendQueue(new Packet14BlockDig(0, par1, par2, par3, par4));
173                    clickBlockCreative(this.mc, this, par1, par2, par3, par4);
174                    this.blockHitDelay = 5;
175                }
176                else if (!this.isHittingBlock || !this.func_85182_a(par1, par2, par3))
177                {
178                    if (this.isHittingBlock)
179                    {
180                        this.netClientHandler.addToSendQueue(new Packet14BlockDig(1, this.currentBlockX, this.currentBlockY, this.currentblockZ, par4));
181                    }
182    
183                    this.netClientHandler.addToSendQueue(new Packet14BlockDig(0, par1, par2, par3, par4));
184                    int var5 = this.mc.theWorld.getBlockId(par1, par2, par3);
185    
186                    if (var5 > 0 && this.curBlockDamageMP == 0.0F)
187                    {
188                        Block.blocksList[var5].onBlockClicked(this.mc.theWorld, par1, par2, par3, this.mc.thePlayer);
189                    }
190    
191                    if (var5 > 0 && Block.blocksList[var5].getPlayerRelativeBlockHardness(this.mc.thePlayer, this.mc.thePlayer.worldObj, par1, par2, par3) >= 1.0F)
192                    {
193                        this.onPlayerDestroyBlock(par1, par2, par3, par4);
194                    }
195                    else
196                    {
197                        this.isHittingBlock = true;
198                        this.currentBlockX = par1;
199                        this.currentBlockY = par2;
200                        this.currentblockZ = par3;
201                        this.field_85183_f = this.mc.thePlayer.getHeldItem();
202                        this.curBlockDamageMP = 0.0F;
203                        this.prevBlockDamageMP = 0.0F;
204                        this.stepSoundTickCounter = 0.0F;
205                        this.mc.theWorld.destroyBlockInWorldPartially(this.mc.thePlayer.entityId, this.currentBlockX, this.currentBlockY, this.currentblockZ, (int)(this.curBlockDamageMP * 10.0F) - 1);
206                    }
207                }
208            }
209        }
210    
211        /**
212         * Resets current block damage and isHittingBlock
213         */
214        public void resetBlockRemoving()
215        {
216            if (this.isHittingBlock)
217            {
218                this.netClientHandler.addToSendQueue(new Packet14BlockDig(1, this.currentBlockX, this.currentBlockY, this.currentblockZ, -1));
219            }
220    
221            this.isHittingBlock = false;
222            this.curBlockDamageMP = 0.0F;
223            this.mc.theWorld.destroyBlockInWorldPartially(this.mc.thePlayer.entityId, this.currentBlockX, this.currentBlockY, this.currentblockZ, -1);
224        }
225    
226        /**
227         * Called when a player damages a block and updates damage counters
228         */
229        public void onPlayerDamageBlock(int par1, int par2, int par3, int par4)
230        {
231            this.syncCurrentPlayItem();
232    
233            if (this.blockHitDelay > 0)
234            {
235                --this.blockHitDelay;
236            }
237            else if (this.currentGameType.isCreative())
238            {
239                this.blockHitDelay = 5;
240                this.netClientHandler.addToSendQueue(new Packet14BlockDig(0, par1, par2, par3, par4));
241                clickBlockCreative(this.mc, this, par1, par2, par3, par4);
242            }
243            else
244            {
245                if (this.func_85182_a(par1, par2, par3))
246                {
247                    int var5 = this.mc.theWorld.getBlockId(par1, par2, par3);
248    
249                    if (var5 == 0)
250                    {
251                        this.isHittingBlock = false;
252                        return;
253                    }
254    
255                    Block var6 = Block.blocksList[var5];
256                    this.curBlockDamageMP += var6.getPlayerRelativeBlockHardness(this.mc.thePlayer, this.mc.thePlayer.worldObj, par1, par2, par3);
257    
258                    if (this.stepSoundTickCounter % 4.0F == 0.0F && var6 != null)
259                    {
260                        this.mc.sndManager.playSound(var6.stepSound.getStepSound(), (float)par1 + 0.5F, (float)par2 + 0.5F, (float)par3 + 0.5F, (var6.stepSound.getVolume() + 1.0F) / 8.0F, var6.stepSound.getPitch() * 0.5F);
261                    }
262    
263                    ++this.stepSoundTickCounter;
264    
265                    if (this.curBlockDamageMP >= 1.0F)
266                    {
267                        this.isHittingBlock = false;
268                        this.netClientHandler.addToSendQueue(new Packet14BlockDig(2, par1, par2, par3, par4));
269                        this.onPlayerDestroyBlock(par1, par2, par3, par4);
270                        this.curBlockDamageMP = 0.0F;
271                        this.prevBlockDamageMP = 0.0F;
272                        this.stepSoundTickCounter = 0.0F;
273                        this.blockHitDelay = 5;
274                    }
275    
276                    this.mc.theWorld.destroyBlockInWorldPartially(this.mc.thePlayer.entityId, this.currentBlockX, this.currentBlockY, this.currentblockZ, (int)(this.curBlockDamageMP * 10.0F) - 1);
277                }
278                else
279                {
280                    this.clickBlock(par1, par2, par3, par4);
281                }
282            }
283        }
284    
285        /**
286         * player reach distance = 4F
287         */
288        public float getBlockReachDistance()
289        {
290            return this.currentGameType.isCreative() ? 5.0F : 4.5F;
291        }
292    
293        public void updateController()
294        {
295            this.syncCurrentPlayItem();
296            this.prevBlockDamageMP = this.curBlockDamageMP;
297            this.mc.sndManager.playRandomMusicIfReady();
298        }
299    
300        private boolean func_85182_a(int par1, int par2, int par3)
301        {
302            return par1 == this.currentBlockX && par2 == this.currentBlockY && par3 == this.currentblockZ && this.field_85183_f == this.mc.thePlayer.getHeldItem();
303        }
304    
305        /**
306         * Syncs the current player item with the server
307         */
308        private void syncCurrentPlayItem()
309        {
310            int var1 = this.mc.thePlayer.inventory.currentItem;
311    
312            if (var1 != this.currentPlayerItem)
313            {
314                this.currentPlayerItem = var1;
315                this.netClientHandler.addToSendQueue(new Packet16BlockItemSwitch(this.currentPlayerItem));
316            }
317        }
318    
319        /**
320         * Handles a players right click. Args: player, world, x, y, z, side, hitVec
321         */
322        public boolean onPlayerRightClick(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack, int par4, int par5, int par6, int par7, Vec3 par8Vec3)
323        {
324            this.syncCurrentPlayItem();
325            float var9 = (float)par8Vec3.xCoord - (float)par4;
326            float var10 = (float)par8Vec3.yCoord - (float)par5;
327            float var11 = (float)par8Vec3.zCoord - (float)par6;
328            boolean var12 = false;
329            int var13 = par2World.getBlockId(par4, par5, par6);
330            if (par3ItemStack != null && 
331                par3ItemStack.getItem() != null && 
332                par3ItemStack.getItem().onItemUseFirst(par3ItemStack, par1EntityPlayer, par2World, par4, par5, par6, par7, var9, var10, var11))
333            {
334                    return true;
335            }
336    
337            if (var13 > 0 && Block.blocksList[var13].onBlockActivated(par2World, par4, par5, par6, par1EntityPlayer, par7, var9, var10, var11))
338            {
339                var12 = true;
340            }
341    
342            if (!var12 && par3ItemStack != null && par3ItemStack.getItem() instanceof ItemBlock)
343            {
344                ItemBlock var14 = (ItemBlock)par3ItemStack.getItem();
345    
346                if (!var14.canPlaceItemBlockOnSide(par2World, par4, par5, par6, par7, par1EntityPlayer, par3ItemStack))
347                {
348                    return false;
349                }
350            }
351    
352            this.netClientHandler.addToSendQueue(new Packet15Place(par4, par5, par6, par7, par1EntityPlayer.inventory.getCurrentItem(), var9, var10, var11));
353    
354            if (var12)
355            {
356                return true;
357            }
358            else if (par3ItemStack == null)
359            {
360                return false;
361            }
362            else if (this.currentGameType.isCreative())
363            {
364                int var17 = par3ItemStack.getItemDamage();
365                int var15 = par3ItemStack.stackSize;
366                boolean var16 = par3ItemStack.tryPlaceItemIntoWorld(par1EntityPlayer, par2World, par4, par5, par6, par7, var9, var10, var11);
367                par3ItemStack.setItemDamage(var17);
368                par3ItemStack.stackSize = var15;
369                return var16;
370            }
371            else
372            {
373                if (!par3ItemStack.tryPlaceItemIntoWorld(par1EntityPlayer, par2World, par4, par5, par6, par7, var9, var10, var11))
374                {
375                    return false;
376                }
377                if (par3ItemStack.stackSize <= 0)
378                {
379                    MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(par1EntityPlayer, par3ItemStack));
380                }
381                return true;
382            }
383        }
384    
385        /**
386         * Notifies the server of things like consuming food, etc...
387         */
388        public boolean sendUseItem(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack)
389        {
390            this.syncCurrentPlayItem();
391            this.netClientHandler.addToSendQueue(new Packet15Place(-1, -1, -1, 255, par1EntityPlayer.inventory.getCurrentItem(), 0.0F, 0.0F, 0.0F));
392            int var4 = par3ItemStack.stackSize;
393            ItemStack var5 = par3ItemStack.useItemRightClick(par2World, par1EntityPlayer);
394    
395            if (var5 == par3ItemStack && (var5 == null || var5.stackSize == var4))
396            {
397                return false;
398            }
399            else
400            {
401                par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = var5;
402    
403                if (var5.stackSize <= 0)
404                {
405                    par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = null;
406                    MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(par1EntityPlayer, var5));
407                }
408    
409                return true;
410            }
411        }
412    
413        public EntityClientPlayerMP func_78754_a(World par1World)
414        {
415            return new EntityClientPlayerMP(this.mc, par1World, this.mc.session, this.netClientHandler);
416        }
417    
418        /**
419         * Attacks an entity
420         */
421        public void attackEntity(EntityPlayer par1EntityPlayer, Entity par2Entity)
422        {
423            this.syncCurrentPlayItem();
424            this.netClientHandler.addToSendQueue(new Packet7UseEntity(par1EntityPlayer.entityId, par2Entity.entityId, 1));
425            par1EntityPlayer.attackTargetEntityWithCurrentItem(par2Entity);
426        }
427    
428        public boolean func_78768_b(EntityPlayer par1EntityPlayer, Entity par2Entity)
429        {
430            this.syncCurrentPlayItem();
431            this.netClientHandler.addToSendQueue(new Packet7UseEntity(par1EntityPlayer.entityId, par2Entity.entityId, 0));
432            return par1EntityPlayer.interactWith(par2Entity);
433        }
434    
435        public ItemStack windowClick(int par1, int par2, int par3, int par4, EntityPlayer par5EntityPlayer)
436        {
437            short var6 = par5EntityPlayer.craftingInventory.getNextTransactionID(par5EntityPlayer.inventory);
438            ItemStack var7 = par5EntityPlayer.craftingInventory.slotClick(par2, par3, par4, par5EntityPlayer);
439            this.netClientHandler.addToSendQueue(new Packet102WindowClick(par1, par2, par3, par4, var7, var6));
440            return var7;
441        }
442    
443        /**
444         * GuiEnchantment uses this during multiplayer to tell PlayerControllerMP to send a packet indicating the
445         * enchantment action the player has taken.
446         */
447        public void sendEnchantPacket(int par1, int par2)
448        {
449            this.netClientHandler.addToSendQueue(new Packet108EnchantItem(par1, par2));
450        }
451    
452        /**
453         * Used in PlayerControllerMP to update the server with an ItemStack in a slot.
454         */
455        public void sendSlotPacket(ItemStack par1ItemStack, int par2)
456        {
457            if (this.currentGameType.isCreative())
458            {
459                this.netClientHandler.addToSendQueue(new Packet107CreativeSetSlot(par2, par1ItemStack));
460            }
461        }
462    
463        public void func_78752_a(ItemStack par1ItemStack)
464        {
465            if (this.currentGameType.isCreative() && par1ItemStack != null)
466            {
467                this.netClientHandler.addToSendQueue(new Packet107CreativeSetSlot(-1, par1ItemStack));
468            }
469        }
470    
471        public void onStoppedUsingItem(EntityPlayer par1EntityPlayer)
472        {
473            this.syncCurrentPlayItem();
474            this.netClientHandler.addToSendQueue(new Packet14BlockDig(5, 0, 0, 0, 255));
475            par1EntityPlayer.stopUsingItem();
476        }
477    
478        public boolean func_78763_f()
479        {
480            return true;
481        }
482    
483        /**
484         * Checks if the player is not creative, used for checking if it should break a block instantly
485         */
486        public boolean isNotCreative()
487        {
488            return !this.currentGameType.isCreative();
489        }
490    
491        /**
492         * returns true if player is in creative mode
493         */
494        public boolean isInCreativeMode()
495        {
496            return this.currentGameType.isCreative();
497        }
498    
499        /**
500         * true for hitting entities far away.
501         */
502        public boolean extendedReach()
503        {
504            return this.currentGameType.isCreative();
505        }
506    }