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