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 }