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 }