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