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