001 package net.minecraft.entity.projectile; 002 003 import cpw.mods.fml.relauncher.Side; 004 import cpw.mods.fml.relauncher.SideOnly; 005 import java.util.List; 006 import net.minecraft.block.Block; 007 import net.minecraft.enchantment.EnchantmentThorns; 008 import net.minecraft.entity.Entity; 009 import net.minecraft.entity.EntityLiving; 010 import net.minecraft.entity.IProjectile; 011 import net.minecraft.entity.monster.EntityEnderman; 012 import net.minecraft.entity.player.EntityPlayer; 013 import net.minecraft.entity.player.EntityPlayerMP; 014 import net.minecraft.item.Item; 015 import net.minecraft.item.ItemStack; 016 import net.minecraft.nbt.NBTTagCompound; 017 import net.minecraft.network.packet.Packet70GameEvent; 018 import net.minecraft.util.AxisAlignedBB; 019 import net.minecraft.util.DamageSource; 020 import net.minecraft.util.MathHelper; 021 import net.minecraft.util.MovingObjectPosition; 022 import net.minecraft.util.Vec3; 023 import net.minecraft.world.World; 024 025 public class EntityArrow extends Entity implements IProjectile 026 { 027 private int xTile = -1; 028 private int yTile = -1; 029 private int zTile = -1; 030 private int inTile = 0; 031 private int inData = 0; 032 private boolean inGround = false; 033 034 /** 1 if the player can pick up the arrow */ 035 public int canBePickedUp = 0; 036 037 /** Seems to be some sort of timer for animating an arrow. */ 038 public int arrowShake = 0; 039 040 /** The owner of this arrow. */ 041 public Entity shootingEntity; 042 private int ticksInGround; 043 private int ticksInAir = 0; 044 private double damage = 2.0D; 045 046 /** The amount of knockback an arrow applies when it hits a mob. */ 047 private int knockbackStrength; 048 049 public EntityArrow(World par1World) 050 { 051 super(par1World); 052 this.renderDistanceWeight = 10.0D; 053 this.setSize(0.5F, 0.5F); 054 } 055 056 public EntityArrow(World par1World, double par2, double par4, double par6) 057 { 058 super(par1World); 059 this.renderDistanceWeight = 10.0D; 060 this.setSize(0.5F, 0.5F); 061 this.setPosition(par2, par4, par6); 062 this.yOffset = 0.0F; 063 } 064 065 public EntityArrow(World par1World, EntityLiving par2EntityLiving, EntityLiving par3EntityLiving, float par4, float par5) 066 { 067 super(par1World); 068 this.renderDistanceWeight = 10.0D; 069 this.shootingEntity = par2EntityLiving; 070 071 if (par2EntityLiving instanceof EntityPlayer) 072 { 073 this.canBePickedUp = 1; 074 } 075 076 this.posY = par2EntityLiving.posY + (double)par2EntityLiving.getEyeHeight() - 0.10000000149011612D; 077 double var6 = par3EntityLiving.posX - par2EntityLiving.posX; 078 double var8 = par3EntityLiving.posY + (double)par3EntityLiving.getEyeHeight() - 0.699999988079071D - this.posY; 079 double var10 = par3EntityLiving.posZ - par2EntityLiving.posZ; 080 double var12 = (double)MathHelper.sqrt_double(var6 * var6 + var10 * var10); 081 082 if (var12 >= 1.0E-7D) 083 { 084 float var14 = (float)(Math.atan2(var10, var6) * 180.0D / Math.PI) - 90.0F; 085 float var15 = (float)(-(Math.atan2(var8, var12) * 180.0D / Math.PI)); 086 double var16 = var6 / var12; 087 double var18 = var10 / var12; 088 this.setLocationAndAngles(par2EntityLiving.posX + var16, this.posY, par2EntityLiving.posZ + var18, var14, var15); 089 this.yOffset = 0.0F; 090 float var20 = (float)var12 * 0.2F; 091 this.setThrowableHeading(var6, var8 + (double)var20, var10, par4, par5); 092 } 093 } 094 095 public EntityArrow(World par1World, EntityLiving par2EntityLiving, float par3) 096 { 097 super(par1World); 098 this.renderDistanceWeight = 10.0D; 099 this.shootingEntity = par2EntityLiving; 100 101 if (par2EntityLiving instanceof EntityPlayer) 102 { 103 this.canBePickedUp = 1; 104 } 105 106 this.setSize(0.5F, 0.5F); 107 this.setLocationAndAngles(par2EntityLiving.posX, par2EntityLiving.posY + (double)par2EntityLiving.getEyeHeight(), par2EntityLiving.posZ, par2EntityLiving.rotationYaw, par2EntityLiving.rotationPitch); 108 this.posX -= (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); 109 this.posY -= 0.10000000149011612D; 110 this.posZ -= (double)(MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); 111 this.setPosition(this.posX, this.posY, this.posZ); 112 this.yOffset = 0.0F; 113 this.motionX = (double)(-MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI)); 114 this.motionZ = (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI)); 115 this.motionY = (double)(-MathHelper.sin(this.rotationPitch / 180.0F * (float)Math.PI)); 116 this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, par3 * 1.5F, 1.0F); 117 } 118 119 protected void entityInit() 120 { 121 this.dataWatcher.addObject(16, Byte.valueOf((byte)0)); 122 } 123 124 /** 125 * Similar to setArrowHeading, it's point the throwable entity to a x, y, z direction. 126 */ 127 public void setThrowableHeading(double par1, double par3, double par5, float par7, float par8) 128 { 129 float var9 = MathHelper.sqrt_double(par1 * par1 + par3 * par3 + par5 * par5); 130 par1 /= (double)var9; 131 par3 /= (double)var9; 132 par5 /= (double)var9; 133 par1 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 134 par3 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 135 par5 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 136 par1 *= (double)par7; 137 par3 *= (double)par7; 138 par5 *= (double)par7; 139 this.motionX = par1; 140 this.motionY = par3; 141 this.motionZ = par5; 142 float var10 = MathHelper.sqrt_double(par1 * par1 + par5 * par5); 143 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI); 144 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)var10) * 180.0D / Math.PI); 145 this.ticksInGround = 0; 146 } 147 148 @SideOnly(Side.CLIENT) 149 150 /** 151 * Sets the position and rotation. Only difference from the other one is no bounding on the rotation. Args: posX, 152 * posY, posZ, yaw, pitch 153 */ 154 public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) 155 { 156 this.setPosition(par1, par3, par5); 157 this.setRotation(par7, par8); 158 } 159 160 @SideOnly(Side.CLIENT) 161 162 /** 163 * Sets the velocity to the args. Args: x, y, z 164 */ 165 public void setVelocity(double par1, double par3, double par5) 166 { 167 this.motionX = par1; 168 this.motionY = par3; 169 this.motionZ = par5; 170 171 if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) 172 { 173 float var7 = MathHelper.sqrt_double(par1 * par1 + par5 * par5); 174 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI); 175 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)var7) * 180.0D / Math.PI); 176 this.prevRotationPitch = this.rotationPitch; 177 this.prevRotationYaw = this.rotationYaw; 178 this.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); 179 this.ticksInGround = 0; 180 } 181 } 182 183 /** 184 * Called to update the entity's position/logic. 185 */ 186 public void onUpdate() 187 { 188 super.onUpdate(); 189 190 if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) 191 { 192 float var1 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 193 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); 194 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(this.motionY, (double)var1) * 180.0D / Math.PI); 195 } 196 197 int var16 = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 198 199 if (var16 > 0) 200 { 201 Block.blocksList[var16].setBlockBoundsBasedOnState(this.worldObj, this.xTile, this.yTile, this.zTile); 202 AxisAlignedBB var2 = Block.blocksList[var16].getCollisionBoundingBoxFromPool(this.worldObj, this.xTile, this.yTile, this.zTile); 203 204 if (var2 != null && var2.isVecInside(this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ))) 205 { 206 this.inGround = true; 207 } 208 } 209 210 if (this.arrowShake > 0) 211 { 212 --this.arrowShake; 213 } 214 215 if (this.inGround) 216 { 217 int var18 = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 218 int var19 = this.worldObj.getBlockMetadata(this.xTile, this.yTile, this.zTile); 219 220 if (var18 == this.inTile && var19 == this.inData) 221 { 222 ++this.ticksInGround; 223 224 if (this.ticksInGround == 1200) 225 { 226 this.setDead(); 227 } 228 } 229 else 230 { 231 this.inGround = false; 232 this.motionX *= (double)(this.rand.nextFloat() * 0.2F); 233 this.motionY *= (double)(this.rand.nextFloat() * 0.2F); 234 this.motionZ *= (double)(this.rand.nextFloat() * 0.2F); 235 this.ticksInGround = 0; 236 this.ticksInAir = 0; 237 } 238 } 239 else 240 { 241 ++this.ticksInAir; 242 Vec3 var17 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 243 Vec3 var3 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 244 MovingObjectPosition var4 = this.worldObj.rayTraceBlocks_do_do(var17, var3, false, true); 245 var17 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 246 var3 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 247 248 if (var4 != null) 249 { 250 var3 = this.worldObj.getWorldVec3Pool().getVecFromPool(var4.hitVec.xCoord, var4.hitVec.yCoord, var4.hitVec.zCoord); 251 } 252 253 Entity var5 = null; 254 List var6 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D)); 255 double var7 = 0.0D; 256 int var9; 257 float var11; 258 259 for (var9 = 0; var9 < var6.size(); ++var9) 260 { 261 Entity var10 = (Entity)var6.get(var9); 262 263 if (var10.canBeCollidedWith() && (var10 != this.shootingEntity || this.ticksInAir >= 5)) 264 { 265 var11 = 0.3F; 266 AxisAlignedBB var12 = var10.boundingBox.expand((double)var11, (double)var11, (double)var11); 267 MovingObjectPosition var13 = var12.calculateIntercept(var17, var3); 268 269 if (var13 != null) 270 { 271 double var14 = var17.distanceTo(var13.hitVec); 272 273 if (var14 < var7 || var7 == 0.0D) 274 { 275 var5 = var10; 276 var7 = var14; 277 } 278 } 279 } 280 } 281 282 if (var5 != null) 283 { 284 var4 = new MovingObjectPosition(var5); 285 } 286 287 float var20; 288 float var26; 289 290 if (var4 != null) 291 { 292 if (var4.entityHit != null) 293 { 294 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); 295 int var23 = MathHelper.ceiling_double_int((double)var20 * this.damage); 296 297 if (this.getIsCritical()) 298 { 299 var23 += this.rand.nextInt(var23 / 2 + 2); 300 } 301 302 DamageSource var21 = null; 303 304 if (this.shootingEntity == null) 305 { 306 var21 = DamageSource.causeArrowDamage(this, this); 307 } 308 else 309 { 310 var21 = DamageSource.causeArrowDamage(this, this.shootingEntity); 311 } 312 313 if (this.isBurning() && !(var4.entityHit instanceof EntityEnderman)) 314 { 315 var4.entityHit.setFire(5); 316 } 317 318 if (var4.entityHit.attackEntityFrom(var21, var23)) 319 { 320 if (var4.entityHit instanceof EntityLiving) 321 { 322 EntityLiving var24 = (EntityLiving)var4.entityHit; 323 324 if (!this.worldObj.isRemote) 325 { 326 var24.setArrowCountInEntity(var24.getArrowCountInEntity() + 1); 327 } 328 329 if (this.knockbackStrength > 0) 330 { 331 var26 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 332 333 if (var26 > 0.0F) 334 { 335 var4.entityHit.addVelocity(this.motionX * (double)this.knockbackStrength * 0.6000000238418579D / (double)var26, 0.1D, this.motionZ * (double)this.knockbackStrength * 0.6000000238418579D / (double)var26); 336 } 337 } 338 339 if (this.shootingEntity != null) 340 { 341 EnchantmentThorns.func_92044_a(this.shootingEntity, var24, this.rand); 342 } 343 344 if (this.shootingEntity != null && var4.entityHit != this.shootingEntity && var4.entityHit instanceof EntityPlayer && this.shootingEntity instanceof EntityPlayerMP) 345 { 346 ((EntityPlayerMP)this.shootingEntity).playerNetServerHandler.sendPacketToPlayer(new Packet70GameEvent(6, 0)); 347 } 348 } 349 350 this.playSound("random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); 351 352 if (!(var4.entityHit instanceof EntityEnderman)) 353 { 354 this.setDead(); 355 } 356 } 357 else 358 { 359 this.motionX *= -0.10000000149011612D; 360 this.motionY *= -0.10000000149011612D; 361 this.motionZ *= -0.10000000149011612D; 362 this.rotationYaw += 180.0F; 363 this.prevRotationYaw += 180.0F; 364 this.ticksInAir = 0; 365 } 366 } 367 else 368 { 369 this.xTile = var4.blockX; 370 this.yTile = var4.blockY; 371 this.zTile = var4.blockZ; 372 this.inTile = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 373 this.inData = this.worldObj.getBlockMetadata(this.xTile, this.yTile, this.zTile); 374 this.motionX = (double)((float)(var4.hitVec.xCoord - this.posX)); 375 this.motionY = (double)((float)(var4.hitVec.yCoord - this.posY)); 376 this.motionZ = (double)((float)(var4.hitVec.zCoord - this.posZ)); 377 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); 378 this.posX -= this.motionX / (double)var20 * 0.05000000074505806D; 379 this.posY -= this.motionY / (double)var20 * 0.05000000074505806D; 380 this.posZ -= this.motionZ / (double)var20 * 0.05000000074505806D; 381 this.playSound("random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); 382 this.inGround = true; 383 this.arrowShake = 7; 384 this.setIsCritical(false); 385 386 if (this.inTile != 0) 387 { 388 Block.blocksList[this.inTile].onEntityCollidedWithBlock(this.worldObj, this.xTile, this.yTile, this.zTile, this); 389 } 390 } 391 } 392 393 if (this.getIsCritical()) 394 { 395 for (var9 = 0; var9 < 4; ++var9) 396 { 397 this.worldObj.spawnParticle("crit", this.posX + this.motionX * (double)var9 / 4.0D, this.posY + this.motionY * (double)var9 / 4.0D, this.posZ + this.motionZ * (double)var9 / 4.0D, -this.motionX, -this.motionY + 0.2D, -this.motionZ); 398 } 399 } 400 401 this.posX += this.motionX; 402 this.posY += this.motionY; 403 this.posZ += this.motionZ; 404 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 405 this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); 406 407 for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)var20) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F) 408 { 409 ; 410 } 411 412 while (this.rotationPitch - this.prevRotationPitch >= 180.0F) 413 { 414 this.prevRotationPitch += 360.0F; 415 } 416 417 while (this.rotationYaw - this.prevRotationYaw < -180.0F) 418 { 419 this.prevRotationYaw -= 360.0F; 420 } 421 422 while (this.rotationYaw - this.prevRotationYaw >= 180.0F) 423 { 424 this.prevRotationYaw += 360.0F; 425 } 426 427 this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; 428 this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; 429 float var22 = 0.99F; 430 var11 = 0.05F; 431 432 if (this.isInWater()) 433 { 434 for (int var25 = 0; var25 < 4; ++var25) 435 { 436 var26 = 0.25F; 437 this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)var26, this.posY - this.motionY * (double)var26, this.posZ - this.motionZ * (double)var26, this.motionX, this.motionY, this.motionZ); 438 } 439 440 var22 = 0.8F; 441 } 442 443 this.motionX *= (double)var22; 444 this.motionY *= (double)var22; 445 this.motionZ *= (double)var22; 446 this.motionY -= (double)var11; 447 this.setPosition(this.posX, this.posY, this.posZ); 448 this.doBlockCollisions(); 449 } 450 } 451 452 /** 453 * (abstract) Protected helper method to write subclass entity data to NBT. 454 */ 455 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 456 { 457 par1NBTTagCompound.setShort("xTile", (short)this.xTile); 458 par1NBTTagCompound.setShort("yTile", (short)this.yTile); 459 par1NBTTagCompound.setShort("zTile", (short)this.zTile); 460 par1NBTTagCompound.setByte("inTile", (byte)this.inTile); 461 par1NBTTagCompound.setByte("inData", (byte)this.inData); 462 par1NBTTagCompound.setByte("shake", (byte)this.arrowShake); 463 par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0)); 464 par1NBTTagCompound.setByte("pickup", (byte)this.canBePickedUp); 465 par1NBTTagCompound.setDouble("damage", this.damage); 466 } 467 468 /** 469 * (abstract) Protected helper method to read subclass entity data from NBT. 470 */ 471 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 472 { 473 this.xTile = par1NBTTagCompound.getShort("xTile"); 474 this.yTile = par1NBTTagCompound.getShort("yTile"); 475 this.zTile = par1NBTTagCompound.getShort("zTile"); 476 this.inTile = par1NBTTagCompound.getByte("inTile") & 255; 477 this.inData = par1NBTTagCompound.getByte("inData") & 255; 478 this.arrowShake = par1NBTTagCompound.getByte("shake") & 255; 479 this.inGround = par1NBTTagCompound.getByte("inGround") == 1; 480 481 if (par1NBTTagCompound.hasKey("damage")) 482 { 483 this.damage = par1NBTTagCompound.getDouble("damage"); 484 } 485 486 if (par1NBTTagCompound.hasKey("pickup")) 487 { 488 this.canBePickedUp = par1NBTTagCompound.getByte("pickup"); 489 } 490 else if (par1NBTTagCompound.hasKey("player")) 491 { 492 this.canBePickedUp = par1NBTTagCompound.getBoolean("player") ? 1 : 0; 493 } 494 } 495 496 /** 497 * Called by a player entity when they collide with an entity 498 */ 499 public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) 500 { 501 if (!this.worldObj.isRemote && this.inGround && this.arrowShake <= 0) 502 { 503 boolean var2 = this.canBePickedUp == 1 || this.canBePickedUp == 2 && par1EntityPlayer.capabilities.isCreativeMode; 504 505 if (this.canBePickedUp == 1 && !par1EntityPlayer.inventory.addItemStackToInventory(new ItemStack(Item.arrow, 1))) 506 { 507 var2 = false; 508 } 509 510 if (var2) 511 { 512 this.playSound("random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); 513 par1EntityPlayer.onItemPickup(this, 1); 514 this.setDead(); 515 } 516 } 517 } 518 519 /** 520 * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to 521 * prevent them from trampling crops 522 */ 523 protected boolean canTriggerWalking() 524 { 525 return false; 526 } 527 528 @SideOnly(Side.CLIENT) 529 public float getShadowSize() 530 { 531 return 0.0F; 532 } 533 534 public void setDamage(double par1) 535 { 536 this.damage = par1; 537 } 538 539 public double getDamage() 540 { 541 return this.damage; 542 } 543 544 /** 545 * Sets the amount of knockback the arrow applies when it hits a mob. 546 */ 547 public void setKnockbackStrength(int par1) 548 { 549 this.knockbackStrength = par1; 550 } 551 552 /** 553 * If returns false, the item will not inflict any damage against entities. 554 */ 555 public boolean canAttackWithItem() 556 { 557 return false; 558 } 559 560 /** 561 * Whether the arrow has a stream of critical hit particles flying behind it. 562 */ 563 public void setIsCritical(boolean par1) 564 { 565 byte var2 = this.dataWatcher.getWatchableObjectByte(16); 566 567 if (par1) 568 { 569 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 1))); 570 } 571 else 572 { 573 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -2))); 574 } 575 } 576 577 /** 578 * Whether the arrow has a stream of critical hit particles flying behind it. 579 */ 580 public boolean getIsCritical() 581 { 582 byte var1 = this.dataWatcher.getWatchableObjectByte(16); 583 return (var1 & 1) != 0; 584 } 585 }