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 var1, double var3, double var5, float var7, float var8) 107 { 108 float var9 = MathHelper.sqrt_double(var1 * var1 + var3 * var3 + var5 * var5); 109 var1 /= (double)var9; 110 var3 /= (double)var9; 111 var5 /= (double)var9; 112 var1 += this.rand.nextGaussian() * 0.007499999832361937D * (double)var8; 113 var3 += this.rand.nextGaussian() * 0.007499999832361937D * (double)var8; 114 var5 += this.rand.nextGaussian() * 0.007499999832361937D * (double)var8; 115 var1 *= (double)var7; 116 var3 *= (double)var7; 117 var5 *= (double)var7; 118 this.motionX = var1; 119 this.motionY = var3; 120 this.motionZ = var5; 121 float var10 = MathHelper.sqrt_double(var1 * var1 + var5 * var5); 122 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(var1, var5) * 180.0D / Math.PI); 123 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(var3, (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.func_82732_R().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.func_82732_R().getVecFromPool(this.posX, this.posY, this.posZ); 222 Vec3 var3 = this.worldObj.func_82732_R().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.func_82732_R().getVecFromPool(this.posX, this.posY, this.posZ); 225 var3 = this.worldObj.func_82732_R().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 226 227 if (var4 != null) 228 { 229 var3 = this.worldObj.func_82732_R().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 ++((EntityLiving)var4.entityHit).arrowHitTempCounter; 301 302 if (this.knockbackStrength > 0) 303 { 304 float var25 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 305 306 if (var25 > 0.0F) 307 { 308 var4.entityHit.addVelocity(this.motionX * (double)this.knockbackStrength * 0.6000000238418579D / (double)var25, 0.1D, this.motionZ * (double)this.knockbackStrength * 0.6000000238418579D / (double)var25); 309 } 310 } 311 } 312 313 this.worldObj.playSoundAtEntity(this, "random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); 314 this.setDead(); 315 } 316 else 317 { 318 this.motionX *= -0.10000000149011612D; 319 this.motionY *= -0.10000000149011612D; 320 this.motionZ *= -0.10000000149011612D; 321 this.rotationYaw += 180.0F; 322 this.prevRotationYaw += 180.0F; 323 this.ticksInAir = 0; 324 } 325 } 326 else 327 { 328 this.xTile = var4.blockX; 329 this.yTile = var4.blockY; 330 this.zTile = var4.blockZ; 331 this.inTile = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 332 this.inData = this.worldObj.getBlockMetadata(this.xTile, this.yTile, this.zTile); 333 this.motionX = (double)((float)(var4.hitVec.xCoord - this.posX)); 334 this.motionY = (double)((float)(var4.hitVec.yCoord - this.posY)); 335 this.motionZ = (double)((float)(var4.hitVec.zCoord - this.posZ)); 336 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); 337 this.posX -= this.motionX / (double)var20 * 0.05000000074505806D; 338 this.posY -= this.motionY / (double)var20 * 0.05000000074505806D; 339 this.posZ -= this.motionZ / (double)var20 * 0.05000000074505806D; 340 this.worldObj.playSoundAtEntity(this, "random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); 341 this.inGround = true; 342 this.arrowShake = 7; 343 this.setIsCritical(false); 344 } 345 } 346 347 if (this.getIsCritical()) 348 { 349 for (int var21 = 0; var21 < 4; ++var21) 350 { 351 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); 352 } 353 } 354 355 this.posX += this.motionX; 356 this.posY += this.motionY; 357 this.posZ += this.motionZ; 358 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 359 this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); 360 361 for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)var20) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F) 362 { 363 ; 364 } 365 366 while (this.rotationPitch - this.prevRotationPitch >= 180.0F) 367 { 368 this.prevRotationPitch += 360.0F; 369 } 370 371 while (this.rotationYaw - this.prevRotationYaw < -180.0F) 372 { 373 this.prevRotationYaw -= 360.0F; 374 } 375 376 while (this.rotationYaw - this.prevRotationYaw >= 180.0F) 377 { 378 this.prevRotationYaw += 360.0F; 379 } 380 381 this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; 382 this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; 383 float var23 = 0.99F; 384 var11 = 0.05F; 385 386 if (this.isInWater()) 387 { 388 for (int var26 = 0; var26 < 4; ++var26) 389 { 390 float var27 = 0.25F; 391 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); 392 } 393 394 var23 = 0.8F; 395 } 396 397 this.motionX *= (double)var23; 398 this.motionY *= (double)var23; 399 this.motionZ *= (double)var23; 400 this.motionY -= (double)var11; 401 this.setPosition(this.posX, this.posY, this.posZ); 402 this.doBlockCollisions(); 403 } 404 } 405 406 /** 407 * (abstract) Protected helper method to write subclass entity data to NBT. 408 */ 409 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 410 { 411 par1NBTTagCompound.setShort("xTile", (short)this.xTile); 412 par1NBTTagCompound.setShort("yTile", (short)this.yTile); 413 par1NBTTagCompound.setShort("zTile", (short)this.zTile); 414 par1NBTTagCompound.setByte("inTile", (byte)this.inTile); 415 par1NBTTagCompound.setByte("inData", (byte)this.inData); 416 par1NBTTagCompound.setByte("shake", (byte)this.arrowShake); 417 par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0)); 418 par1NBTTagCompound.setByte("pickup", (byte)this.canBePickedUp); 419 par1NBTTagCompound.setDouble("damage", this.damage); 420 } 421 422 /** 423 * (abstract) Protected helper method to read subclass entity data from NBT. 424 */ 425 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 426 { 427 this.xTile = par1NBTTagCompound.getShort("xTile"); 428 this.yTile = par1NBTTagCompound.getShort("yTile"); 429 this.zTile = par1NBTTagCompound.getShort("zTile"); 430 this.inTile = par1NBTTagCompound.getByte("inTile") & 255; 431 this.inData = par1NBTTagCompound.getByte("inData") & 255; 432 this.arrowShake = par1NBTTagCompound.getByte("shake") & 255; 433 this.inGround = par1NBTTagCompound.getByte("inGround") == 1; 434 435 if (par1NBTTagCompound.hasKey("damage")) 436 { 437 this.damage = par1NBTTagCompound.getDouble("damage"); 438 } 439 440 if (par1NBTTagCompound.hasKey("pickup")) 441 { 442 this.canBePickedUp = par1NBTTagCompound.getByte("pickup"); 443 } 444 else if (par1NBTTagCompound.hasKey("player")) 445 { 446 this.canBePickedUp = par1NBTTagCompound.getBoolean("player") ? 1 : 0; 447 } 448 } 449 450 /** 451 * Called by a player entity when they collide with an entity 452 */ 453 public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) 454 { 455 if (!this.worldObj.isRemote && this.inGround && this.arrowShake <= 0) 456 { 457 boolean var2 = this.canBePickedUp == 1 || this.canBePickedUp == 2 && par1EntityPlayer.capabilities.isCreativeMode; 458 459 if (this.canBePickedUp == 1 && !par1EntityPlayer.inventory.addItemStackToInventory(new ItemStack(Item.arrow, 1))) 460 { 461 var2 = false; 462 } 463 464 if (var2) 465 { 466 this.worldObj.playSoundAtEntity(this, "random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); 467 par1EntityPlayer.onItemPickup(this, 1); 468 this.setDead(); 469 } 470 } 471 } 472 473 /** 474 * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to 475 * prevent them from trampling crops 476 */ 477 protected boolean canTriggerWalking() 478 { 479 return false; 480 } 481 482 @SideOnly(Side.CLIENT) 483 public float getShadowSize() 484 { 485 return 0.0F; 486 } 487 488 public void setDamage(double par1) 489 { 490 this.damage = par1; 491 } 492 493 public double getDamage() 494 { 495 return this.damage; 496 } 497 498 /** 499 * Sets the amount of knockback the arrow applies when it hits a mob. 500 */ 501 public void setKnockbackStrength(int par1) 502 { 503 this.knockbackStrength = par1; 504 } 505 506 /** 507 * If returns false, the item will not inflict any damage against entities. 508 */ 509 public boolean canAttackWithItem() 510 { 511 return false; 512 } 513 514 /** 515 * Whether the arrow has a stream of critical hit particles flying behind it. 516 */ 517 public void setIsCritical(boolean par1) 518 { 519 byte var2 = this.dataWatcher.getWatchableObjectByte(16); 520 521 if (par1) 522 { 523 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 1))); 524 } 525 else 526 { 527 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -2))); 528 } 529 } 530 531 /** 532 * Whether the arrow has a stream of critical hit particles flying behind it. 533 */ 534 public boolean getIsCritical() 535 { 536 byte var1 = this.dataWatcher.getWatchableObjectByte(16); 537 return (var1 & 1) != 0; 538 } 539 }