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