    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    }