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