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