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