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