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