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