    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    }