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 }