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