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 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.setArrowHeading(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.setArrowHeading(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 * Uses the provided coordinates as a heading and determines the velocity from it with the set force and random 105 * variance. Args: x, y, z, force, forceVariation 106 */ 107 public void setArrowHeading(double par1, double par3, double par5, float par7, float par8) 108 { 109 float var9 = MathHelper.sqrt_double(par1 * par1 + par3 * par3 + par5 * par5); 110 par1 /= (double)var9; 111 par3 /= (double)var9; 112 par5 /= (double)var9; 113 par1 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 114 par3 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 115 par5 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 116 par1 *= (double)par7; 117 par3 *= (double)par7; 118 par5 *= (double)par7; 119 this.motionX = par1; 120 this.motionY = par3; 121 this.motionZ = par5; 122 float var10 = MathHelper.sqrt_double(par1 * par1 + par5 * par5); 123 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI); 124 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)var10) * 180.0D / Math.PI); 125 this.ticksInGround = 0; 126 } 127 128 @SideOnly(Side.CLIENT) 129 130 /** 131 * Sets the position and rotation. Only difference from the other one is no bounding on the rotation. Args: posX, 132 * posY, posZ, yaw, pitch 133 */ 134 public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) 135 { 136 this.setPosition(par1, par3, par5); 137 this.setRotation(par7, par8); 138 } 139 140 @SideOnly(Side.CLIENT) 141 142 /** 143 * Sets the velocity to the args. Args: x, y, z 144 */ 145 public void setVelocity(double par1, double par3, double par5) 146 { 147 this.motionX = par1; 148 this.motionY = par3; 149 this.motionZ = par5; 150 151 if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) 152 { 153 float var7 = MathHelper.sqrt_double(par1 * par1 + par5 * par5); 154 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI); 155 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)var7) * 180.0D / Math.PI); 156 this.prevRotationPitch = this.rotationPitch; 157 this.prevRotationYaw = this.rotationYaw; 158 this.setLocationAndAngles(this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch); 159 this.ticksInGround = 0; 160 } 161 } 162 163 /** 164 * Called to update the entity's position/logic. 165 */ 166 public void onUpdate() 167 { 168 super.onUpdate(); 169 170 if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) 171 { 172 float var1 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 173 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); 174 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(this.motionY, (double)var1) * 180.0D / Math.PI); 175 } 176 177 int var16 = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 178 179 if (var16 > 0) 180 { 181 Block.blocksList[var16].setBlockBoundsBasedOnState(this.worldObj, this.xTile, this.yTile, this.zTile); 182 AxisAlignedBB var2 = Block.blocksList[var16].getCollisionBoundingBoxFromPool(this.worldObj, this.xTile, this.yTile, this.zTile); 183 184 if (var2 != null && var2.isVecInside(Vec3.getVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ))) 185 { 186 this.inGround = true; 187 } 188 } 189 190 if (this.arrowShake > 0) 191 { 192 --this.arrowShake; 193 } 194 195 if (this.inGround) 196 { 197 int var18 = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 198 int var19 = this.worldObj.getBlockMetadata(this.xTile, this.yTile, this.zTile); 199 200 if (var18 == this.inTile && var19 == this.inData) 201 { 202 ++this.ticksInGround; 203 204 if (this.ticksInGround == 1200) 205 { 206 this.setDead(); 207 } 208 } 209 else 210 { 211 this.inGround = false; 212 this.motionX *= (double)(this.rand.nextFloat() * 0.2F); 213 this.motionY *= (double)(this.rand.nextFloat() * 0.2F); 214 this.motionZ *= (double)(this.rand.nextFloat() * 0.2F); 215 this.ticksInGround = 0; 216 this.ticksInAir = 0; 217 } 218 } 219 else 220 { 221 ++this.ticksInAir; 222 Vec3 var17 = Vec3.getVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 223 Vec3 var3 = Vec3.getVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 224 MovingObjectPosition var4 = this.worldObj.rayTraceBlocks_do_do(var17, var3, false, true); 225 var17 = Vec3.getVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 226 var3 = Vec3.getVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 227 228 if (var4 != null) 229 { 230 var3 = Vec3.getVec3Pool().getVecFromPool(var4.hitVec.xCoord, var4.hitVec.yCoord, var4.hitVec.zCoord); 231 } 232 233 Entity var5 = null; 234 List var6 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D)); 235 double var7 = 0.0D; 236 Iterator var9 = var6.iterator(); 237 float var11; 238 239 while (var9.hasNext()) 240 { 241 Entity var10 = (Entity)var9.next(); 242 243 if (var10.canBeCollidedWith() && (var10 != this.shootingEntity || this.ticksInAir >= 5)) 244 { 245 var11 = 0.3F; 246 AxisAlignedBB var12 = var10.boundingBox.expand((double)var11, (double)var11, (double)var11); 247 MovingObjectPosition var13 = var12.calculateIntercept(var17, var3); 248 249 if (var13 != null) 250 { 251 double var14 = var17.distanceTo(var13.hitVec); 252 253 if (var14 < var7 || var7 == 0.0D) 254 { 255 var5 = var10; 256 var7 = var14; 257 } 258 } 259 } 260 } 261 262 if (var5 != null) 263 { 264 var4 = new MovingObjectPosition(var5); 265 } 266 267 float var20; 268 269 if (var4 != null) 270 { 271 if (var4.entityHit != null) 272 { 273 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); 274 int var24 = MathHelper.ceiling_double_int((double)var20 * this.damage); 275 276 if (this.func_70241_g()) 277 { 278 var24 += this.rand.nextInt(var24 / 2 + 2); 279 } 280 281 DamageSource var22 = null; 282 283 if (this.shootingEntity == null) 284 { 285 var22 = DamageSource.causeArrowDamage(this, this); 286 } 287 else 288 { 289 var22 = DamageSource.causeArrowDamage(this, this.shootingEntity); 290 } 291 292 if (this.isBurning()) 293 { 294 var4.entityHit.setFire(5); 295 } 296 297 if (var4.entityHit.attackEntityFrom(var22, var24)) 298 { 299 if (var4.entityHit instanceof EntityLiving) 300 { 301 ++((EntityLiving)var4.entityHit).arrowHitTempCounter; 302 303 if (this.knockbackStrength > 0) 304 { 305 float var25 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 306 307 if (var25 > 0.0F) 308 { 309 var4.entityHit.addVelocity(this.motionX * (double)this.knockbackStrength * 0.6000000238418579D / (double)var25, 0.1D, this.motionZ * (double)this.knockbackStrength * 0.6000000238418579D / (double)var25); 310 } 311 } 312 } 313 314 this.worldObj.playSoundAtEntity(this, "random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); 315 this.setDead(); 316 } 317 else 318 { 319 this.motionX *= -0.10000000149011612D; 320 this.motionY *= -0.10000000149011612D; 321 this.motionZ *= -0.10000000149011612D; 322 this.rotationYaw += 180.0F; 323 this.prevRotationYaw += 180.0F; 324 this.ticksInAir = 0; 325 } 326 } 327 else 328 { 329 this.xTile = var4.blockX; 330 this.yTile = var4.blockY; 331 this.zTile = var4.blockZ; 332 this.inTile = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 333 this.inData = this.worldObj.getBlockMetadata(this.xTile, this.yTile, this.zTile); 334 this.motionX = (double)((float)(var4.hitVec.xCoord - this.posX)); 335 this.motionY = (double)((float)(var4.hitVec.yCoord - this.posY)); 336 this.motionZ = (double)((float)(var4.hitVec.zCoord - this.posZ)); 337 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); 338 this.posX -= this.motionX / (double)var20 * 0.05000000074505806D; 339 this.posY -= this.motionY / (double)var20 * 0.05000000074505806D; 340 this.posZ -= this.motionZ / (double)var20 * 0.05000000074505806D; 341 this.worldObj.playSoundAtEntity(this, "random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); 342 this.inGround = true; 343 this.arrowShake = 7; 344 this.func_70243_d(false); 345 } 346 } 347 348 if (this.func_70241_g()) 349 { 350 for (int var21 = 0; var21 < 4; ++var21) 351 { 352 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); 353 } 354 } 355 356 this.posX += this.motionX; 357 this.posY += this.motionY; 358 this.posZ += this.motionZ; 359 var20 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 360 this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); 361 362 for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)var20) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F) 363 { 364 ; 365 } 366 367 while (this.rotationPitch - this.prevRotationPitch >= 180.0F) 368 { 369 this.prevRotationPitch += 360.0F; 370 } 371 372 while (this.rotationYaw - this.prevRotationYaw < -180.0F) 373 { 374 this.prevRotationYaw -= 360.0F; 375 } 376 377 while (this.rotationYaw - this.prevRotationYaw >= 180.0F) 378 { 379 this.prevRotationYaw += 360.0F; 380 } 381 382 this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; 383 this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; 384 float var23 = 0.99F; 385 var11 = 0.05F; 386 387 if (this.isInWater()) 388 { 389 for (int var26 = 0; var26 < 4; ++var26) 390 { 391 float var27 = 0.25F; 392 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); 393 } 394 395 var23 = 0.8F; 396 } 397 398 this.motionX *= (double)var23; 399 this.motionY *= (double)var23; 400 this.motionZ *= (double)var23; 401 this.motionY -= (double)var11; 402 this.setPosition(this.posX, this.posY, this.posZ); 403 this.doBlockCollisions(); 404 } 405 } 406 407 /** 408 * (abstract) Protected helper method to write subclass entity data to NBT. 409 */ 410 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 411 { 412 par1NBTTagCompound.setShort("xTile", (short)this.xTile); 413 par1NBTTagCompound.setShort("yTile", (short)this.yTile); 414 par1NBTTagCompound.setShort("zTile", (short)this.zTile); 415 par1NBTTagCompound.setByte("inTile", (byte)this.inTile); 416 par1NBTTagCompound.setByte("inData", (byte)this.inData); 417 par1NBTTagCompound.setByte("shake", (byte)this.arrowShake); 418 par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0)); 419 par1NBTTagCompound.setByte("pickup", (byte)this.canBePickedUp); 420 par1NBTTagCompound.setDouble("damage", this.damage); 421 } 422 423 /** 424 * (abstract) Protected helper method to read subclass entity data from NBT. 425 */ 426 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 427 { 428 this.xTile = par1NBTTagCompound.getShort("xTile"); 429 this.yTile = par1NBTTagCompound.getShort("yTile"); 430 this.zTile = par1NBTTagCompound.getShort("zTile"); 431 this.inTile = par1NBTTagCompound.getByte("inTile") & 255; 432 this.inData = par1NBTTagCompound.getByte("inData") & 255; 433 this.arrowShake = par1NBTTagCompound.getByte("shake") & 255; 434 this.inGround = par1NBTTagCompound.getByte("inGround") == 1; 435 436 if (par1NBTTagCompound.hasKey("damage")) 437 { 438 this.damage = par1NBTTagCompound.getDouble("damage"); 439 } 440 441 if (par1NBTTagCompound.hasKey("pickup")) 442 { 443 this.canBePickedUp = par1NBTTagCompound.getByte("pickup"); 444 } 445 else if (par1NBTTagCompound.hasKey("player")) 446 { 447 this.canBePickedUp = par1NBTTagCompound.getBoolean("player") ? 1 : 0; 448 } 449 } 450 451 /** 452 * Called by a player entity when they collide with an entity 453 */ 454 public void onCollideWithPlayer(EntityPlayer par1EntityPlayer) 455 { 456 if (!this.worldObj.isRemote && this.inGround && this.arrowShake <= 0) 457 { 458 boolean var2 = this.canBePickedUp == 1 || this.canBePickedUp == 2 && par1EntityPlayer.capabilities.isCreativeMode; 459 460 if (this.canBePickedUp == 1 && !par1EntityPlayer.inventory.addItemStackToInventory(new ItemStack(Item.arrow, 1))) 461 { 462 var2 = false; 463 } 464 465 if (var2) 466 { 467 this.worldObj.playSoundAtEntity(this, "random.pop", 0.2F, ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.7F + 1.0F) * 2.0F); 468 par1EntityPlayer.onItemPickup(this, 1); 469 this.setDead(); 470 } 471 } 472 } 473 474 @SideOnly(Side.CLIENT) 475 public float getShadowSize() 476 { 477 return 0.0F; 478 } 479 480 public void setDamage(double par1) 481 { 482 this.damage = par1; 483 } 484 485 public double getDamage() 486 { 487 return this.damage; 488 } 489 490 /** 491 * Sets the amount of knockback the arrow applies when it hits a mob. 492 */ 493 public void setKnockbackStrength(int par1) 494 { 495 this.knockbackStrength = par1; 496 } 497 498 /** 499 * If returns false, the item will not inflict any damage against entities. 500 */ 501 public boolean canAttackWithItem() 502 { 503 return false; 504 } 505 506 public void func_70243_d(boolean par1) 507 { 508 byte var2 = this.dataWatcher.getWatchableObjectByte(16); 509 510 if (par1) 511 { 512 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 | 1))); 513 } 514 else 515 { 516 this.dataWatcher.updateObject(16, Byte.valueOf((byte)(var2 & -2))); 517 } 518 } 519 520 public boolean func_70241_g() 521 { 522 byte var1 = this.dataWatcher.getWatchableObjectByte(16); 523 return (var1 & 1) != 0; 524 } 525 }