001    package net.minecraft.client.multiplayer;
002    
003    import cpw.mods.fml.relauncher.Side;
004    import cpw.mods.fml.relauncher.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;
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 (!par1EntityPlayer.isSneaking() || par1EntityPlayer.getHeldItem() == null)
355            {
356                var13 = par2World.getBlockId(par4, par5, par6);
357    
358                if (var13 > 0 && Block.blocksList[var13].onBlockActivated(par2World, par4, par5, par6, par1EntityPlayer, par7, var9, var10, var11))
359                {
360                    var12 = true;
361                }
362            }
363    
364    
365            if (!var12 && par3ItemStack != null && par3ItemStack.getItem() instanceof ItemBlock)
366            {
367                ItemBlock var16 = (ItemBlock)par3ItemStack.getItem();
368    
369                if (!var16.canPlaceItemBlockOnSide(par2World, par4, par5, par6, par7, par1EntityPlayer, par3ItemStack))
370                {
371                    return false;
372                }
373            }
374    
375            this.netClientHandler.addToSendQueue(new Packet15Place(par4, par5, par6, par7, par1EntityPlayer.inventory.getCurrentItem(), var9, var10, var11));
376    
377            if (var12)
378            {
379                return true;
380            }
381            else if (par3ItemStack == null)
382            {
383                return false;
384            }
385            else if (this.currentGameType.isCreative())
386            {
387                var13 = par3ItemStack.getItemDamage();
388                int var14 = par3ItemStack.stackSize;
389                boolean var15 = par3ItemStack.tryPlaceItemIntoWorld(par1EntityPlayer, par2World, par4, par5, par6, par7, var9, var10, var11);
390                par3ItemStack.setItemDamage(var13);
391                par3ItemStack.stackSize = var14;
392                return var15;
393            }
394            else
395            {
396                if (!par3ItemStack.tryPlaceItemIntoWorld(par1EntityPlayer, par2World, par4, par5, par6, par7, var9, var10, var11))
397                {
398                    return false;
399                }
400                if (par3ItemStack.stackSize <= 0)
401                {
402                    MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(par1EntityPlayer, par3ItemStack));
403                }
404                return true;
405            }
406        }
407    
408        /**
409         * Notifies the server of things like consuming food, etc...
410         */
411        public boolean sendUseItem(EntityPlayer par1EntityPlayer, World par2World, ItemStack par3ItemStack)
412        {
413            this.syncCurrentPlayItem();
414            this.netClientHandler.addToSendQueue(new Packet15Place(-1, -1, -1, 255, par1EntityPlayer.inventory.getCurrentItem(), 0.0F, 0.0F, 0.0F));
415            int var4 = par3ItemStack.stackSize;
416            ItemStack var5 = par3ItemStack.useItemRightClick(par2World, par1EntityPlayer);
417    
418            if (var5 == par3ItemStack && (var5 == null || var5.stackSize == var4))
419            {
420                return false;
421            }
422            else
423            {
424                par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = var5;
425    
426                if (var5.stackSize <= 0)
427                {
428                    par1EntityPlayer.inventory.mainInventory[par1EntityPlayer.inventory.currentItem] = null;
429                    MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(par1EntityPlayer, var5));
430                }
431    
432                return true;
433            }
434        }
435    
436        public EntityClientPlayerMP func_78754_a(World par1World)
437        {
438            return new EntityClientPlayerMP(this.mc, par1World, this.mc.session, this.netClientHandler);
439        }
440    
441        /**
442         * Attacks an entity
443         */
444        public void attackEntity(EntityPlayer par1EntityPlayer, Entity par2Entity)
445        {
446            this.syncCurrentPlayItem();
447            this.netClientHandler.addToSendQueue(new Packet7UseEntity(par1EntityPlayer.entityId, par2Entity.entityId, 1));
448            par1EntityPlayer.attackTargetEntityWithCurrentItem(par2Entity);
449        }
450    
451        public boolean func_78768_b(EntityPlayer par1EntityPlayer, Entity par2Entity)
452        {
453            this.syncCurrentPlayItem();
454            this.netClientHandler.addToSendQueue(new Packet7UseEntity(par1EntityPlayer.entityId, par2Entity.entityId, 0));
455            return par1EntityPlayer.interactWith(par2Entity);
456        }
457    
458        public ItemStack windowClick(int par1, int par2, int par3, int par4, EntityPlayer par5EntityPlayer)
459        {
460            short var6 = par5EntityPlayer.openContainer.getNextTransactionID(par5EntityPlayer.inventory);
461            ItemStack var7 = par5EntityPlayer.openContainer.slotClick(par2, par3, par4, par5EntityPlayer);
462            this.netClientHandler.addToSendQueue(new Packet102WindowClick(par1, par2, par3, par4, var7, var6));
463            return var7;
464        }
465    
466        /**
467         * GuiEnchantment uses this during multiplayer to tell PlayerControllerMP to send a packet indicating the
468         * enchantment action the player has taken.
469         */
470        public void sendEnchantPacket(int par1, int par2)
471        {
472            this.netClientHandler.addToSendQueue(new Packet108EnchantItem(par1, par2));
473        }
474    
475        /**
476         * Used in PlayerControllerMP to update the server with an ItemStack in a slot.
477         */
478        public void sendSlotPacket(ItemStack par1ItemStack, int par2)
479        {
480            if (this.currentGameType.isCreative())
481            {
482                this.netClientHandler.addToSendQueue(new Packet107CreativeSetSlot(par2, par1ItemStack));
483            }
484        }
485    
486        public void func_78752_a(ItemStack par1ItemStack)
487        {
488            if (this.currentGameType.isCreative() && par1ItemStack != null)
489            {
490                this.netClientHandler.addToSendQueue(new Packet107CreativeSetSlot(-1, par1ItemStack));
491            }
492        }
493    
494        public void onStoppedUsingItem(EntityPlayer par1EntityPlayer)
495        {
496            this.syncCurrentPlayItem();
497            this.netClientHandler.addToSendQueue(new Packet14BlockDig(5, 0, 0, 0, 255));
498            par1EntityPlayer.stopUsingItem();
499        }
500    
501        public boolean func_78763_f()
502        {
503            return true;
504        }
505    
506        /**
507         * Checks if the player is not creative, used for checking if it should break a block instantly
508         */
509        public boolean isNotCreative()
510        {
511            return !this.currentGameType.isCreative();
512        }
513    
514        /**
515         * returns true if player is in creative mode
516         */
517        public boolean isInCreativeMode()
518        {
519            return this.currentGameType.isCreative();
520        }
521    
522        /**
523         * true for hitting entities far away.
524         */
525        public boolean extendedReach()
526        {
527            return this.currentGameType.isCreative();
528        }
529    }