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 abstract class EntityThrowable 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 protected boolean inGround = false; 015 public int throwableShake = 0; 016 017 /** 018 * Is the entity that throws this 'thing' (snowball, ender pearl, eye of ender or potion) 019 */ 020 private EntityLiving thrower; 021 private String field_85053_h = null; 022 private int ticksInGround; 023 private int ticksInAir = 0; 024 025 public EntityThrowable(World par1World) 026 { 027 super(par1World); 028 this.setSize(0.25F, 0.25F); 029 } 030 031 protected void entityInit() {} 032 033 @SideOnly(Side.CLIENT) 034 035 /** 036 * Checks if the entity is in range to render by using the past in distance and comparing it to its average edge 037 * length * 64 * renderDistanceWeight Args: distance 038 */ 039 public boolean isInRangeToRenderDist(double par1) 040 { 041 double var3 = this.boundingBox.getAverageEdgeLength() * 4.0D; 042 var3 *= 64.0D; 043 return par1 < var3 * var3; 044 } 045 046 public EntityThrowable(World par1World, EntityLiving par2EntityLiving) 047 { 048 super(par1World); 049 this.thrower = par2EntityLiving; 050 this.setSize(0.25F, 0.25F); 051 this.setLocationAndAngles(par2EntityLiving.posX, par2EntityLiving.posY + (double)par2EntityLiving.getEyeHeight(), par2EntityLiving.posZ, par2EntityLiving.rotationYaw, par2EntityLiving.rotationPitch); 052 this.posX -= (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); 053 this.posY -= 0.10000000149011612D; 054 this.posZ -= (double)(MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); 055 this.setPosition(this.posX, this.posY, this.posZ); 056 this.yOffset = 0.0F; 057 float var3 = 0.4F; 058 this.motionX = (double)(-MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI) * var3); 059 this.motionZ = (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI) * var3); 060 this.motionY = (double)(-MathHelper.sin((this.rotationPitch + this.func_70183_g()) / 180.0F * (float)Math.PI) * var3); 061 this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, this.func_70182_d(), 1.0F); 062 } 063 064 public EntityThrowable(World par1World, double par2, double par4, double par6) 065 { 066 super(par1World); 067 this.ticksInGround = 0; 068 this.setSize(0.25F, 0.25F); 069 this.setPosition(par2, par4, par6); 070 this.yOffset = 0.0F; 071 } 072 073 protected float func_70182_d() 074 { 075 return 1.5F; 076 } 077 078 protected float func_70183_g() 079 { 080 return 0.0F; 081 } 082 083 /** 084 * Similar to setArrowHeading, it's point the throwable entity to a x, y, z direction. 085 */ 086 public void setThrowableHeading(double par1, double par3, double par5, float par7, float par8) 087 { 088 float var9 = MathHelper.sqrt_double(par1 * par1 + par3 * par3 + par5 * par5); 089 par1 /= (double)var9; 090 par3 /= (double)var9; 091 par5 /= (double)var9; 092 par1 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 093 par3 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 094 par5 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8; 095 par1 *= (double)par7; 096 par3 *= (double)par7; 097 par5 *= (double)par7; 098 this.motionX = par1; 099 this.motionY = par3; 100 this.motionZ = par5; 101 float var10 = MathHelper.sqrt_double(par1 * par1 + par5 * par5); 102 this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI); 103 this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)var10) * 180.0D / Math.PI); 104 this.ticksInGround = 0; 105 } 106 107 @SideOnly(Side.CLIENT) 108 109 /** 110 * Sets the velocity to the args. Args: x, y, z 111 */ 112 public void setVelocity(double par1, double par3, double par5) 113 { 114 this.motionX = par1; 115 this.motionY = par3; 116 this.motionZ = par5; 117 118 if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F) 119 { 120 float var7 = 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)var7) * 180.0D / Math.PI); 123 } 124 } 125 126 /** 127 * Called to update the entity's position/logic. 128 */ 129 public void onUpdate() 130 { 131 this.lastTickPosX = this.posX; 132 this.lastTickPosY = this.posY; 133 this.lastTickPosZ = this.posZ; 134 super.onUpdate(); 135 136 if (this.throwableShake > 0) 137 { 138 --this.throwableShake; 139 } 140 141 if (this.inGround) 142 { 143 int var1 = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 144 145 if (var1 == this.inTile) 146 { 147 ++this.ticksInGround; 148 149 if (this.ticksInGround == 1200) 150 { 151 this.setDead(); 152 } 153 154 return; 155 } 156 157 this.inGround = false; 158 this.motionX *= (double)(this.rand.nextFloat() * 0.2F); 159 this.motionY *= (double)(this.rand.nextFloat() * 0.2F); 160 this.motionZ *= (double)(this.rand.nextFloat() * 0.2F); 161 this.ticksInGround = 0; 162 this.ticksInAir = 0; 163 } 164 else 165 { 166 ++this.ticksInAir; 167 } 168 169 Vec3 var16 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 170 Vec3 var2 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 171 MovingObjectPosition var3 = this.worldObj.rayTraceBlocks(var16, var2); 172 var16 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 173 var2 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 174 175 if (var3 != null) 176 { 177 var2 = this.worldObj.getWorldVec3Pool().getVecFromPool(var3.hitVec.xCoord, var3.hitVec.yCoord, var3.hitVec.zCoord); 178 } 179 180 if (!this.worldObj.isRemote) 181 { 182 Entity var4 = null; 183 List var5 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D)); 184 double var6 = 0.0D; 185 EntityLiving var8 = this.func_85052_h(); 186 Iterator var9 = var5.iterator(); 187 188 while (var9.hasNext()) 189 { 190 Entity var10 = (Entity)var9.next(); 191 192 if (var10.canBeCollidedWith() && (var10 != var8 || this.ticksInAir >= 5)) 193 { 194 float var11 = 0.3F; 195 AxisAlignedBB var12 = var10.boundingBox.expand((double)var11, (double)var11, (double)var11); 196 MovingObjectPosition var13 = var12.calculateIntercept(var16, var2); 197 198 if (var13 != null) 199 { 200 double var14 = var16.distanceTo(var13.hitVec); 201 202 if (var14 < var6 || var6 == 0.0D) 203 { 204 var4 = var10; 205 var6 = var14; 206 } 207 } 208 } 209 } 210 211 if (var4 != null) 212 { 213 var3 = new MovingObjectPosition(var4); 214 } 215 } 216 217 if (var3 != null) 218 { 219 if (var3.typeOfHit == EnumMovingObjectType.TILE && this.worldObj.getBlockId(var3.blockX, var3.blockY, var3.blockZ) == Block.portal.blockID) 220 { 221 this.setInPortal(); 222 } 223 else 224 { 225 this.onImpact(var3); 226 } 227 } 228 229 this.posX += this.motionX; 230 this.posY += this.motionY; 231 this.posZ += this.motionZ; 232 float var17 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 233 this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI); 234 235 for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)var17) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F) 236 { 237 ; 238 } 239 240 while (this.rotationPitch - this.prevRotationPitch >= 180.0F) 241 { 242 this.prevRotationPitch += 360.0F; 243 } 244 245 while (this.rotationYaw - this.prevRotationYaw < -180.0F) 246 { 247 this.prevRotationYaw -= 360.0F; 248 } 249 250 while (this.rotationYaw - this.prevRotationYaw >= 180.0F) 251 { 252 this.prevRotationYaw += 360.0F; 253 } 254 255 this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; 256 this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; 257 float var18 = 0.99F; 258 float var19 = this.getGravityVelocity(); 259 260 if (this.isInWater()) 261 { 262 for (int var7 = 0; var7 < 4; ++var7) 263 { 264 float var20 = 0.25F; 265 this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)var20, this.posY - this.motionY * (double)var20, this.posZ - this.motionZ * (double)var20, this.motionX, this.motionY, this.motionZ); 266 } 267 268 var18 = 0.8F; 269 } 270 271 this.motionX *= (double)var18; 272 this.motionY *= (double)var18; 273 this.motionZ *= (double)var18; 274 this.motionY -= (double)var19; 275 this.setPosition(this.posX, this.posY, this.posZ); 276 } 277 278 /** 279 * Gets the amount of gravity to apply to the thrown entity with each tick. 280 */ 281 protected float getGravityVelocity() 282 { 283 return 0.03F; 284 } 285 286 /** 287 * Called when this EntityThrowable hits a block or entity. 288 */ 289 protected abstract void onImpact(MovingObjectPosition var1); 290 291 /** 292 * (abstract) Protected helper method to write subclass entity data to NBT. 293 */ 294 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 295 { 296 par1NBTTagCompound.setShort("xTile", (short)this.xTile); 297 par1NBTTagCompound.setShort("yTile", (short)this.yTile); 298 par1NBTTagCompound.setShort("zTile", (short)this.zTile); 299 par1NBTTagCompound.setByte("inTile", (byte)this.inTile); 300 par1NBTTagCompound.setByte("shake", (byte)this.throwableShake); 301 par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0)); 302 303 if ((this.field_85053_h == null || this.field_85053_h.length() == 0) && this.thrower != null && this.thrower instanceof EntityPlayer) 304 { 305 this.field_85053_h = this.thrower.getEntityName(); 306 } 307 308 par1NBTTagCompound.setString("ownerName", this.field_85053_h == null ? "" : this.field_85053_h); 309 } 310 311 /** 312 * (abstract) Protected helper method to read subclass entity data from NBT. 313 */ 314 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 315 { 316 this.xTile = par1NBTTagCompound.getShort("xTile"); 317 this.yTile = par1NBTTagCompound.getShort("yTile"); 318 this.zTile = par1NBTTagCompound.getShort("zTile"); 319 this.inTile = par1NBTTagCompound.getByte("inTile") & 255; 320 this.throwableShake = par1NBTTagCompound.getByte("shake") & 255; 321 this.inGround = par1NBTTagCompound.getByte("inGround") == 1; 322 this.field_85053_h = par1NBTTagCompound.getString("ownerName"); 323 324 if (this.field_85053_h != null && this.field_85053_h.length() == 0) 325 { 326 this.field_85053_h = null; 327 } 328 } 329 330 @SideOnly(Side.CLIENT) 331 public float getShadowSize() 332 { 333 return 0.0F; 334 } 335 336 public EntityLiving func_85052_h() 337 { 338 if (this.thrower == null && this.field_85053_h != null && this.field_85053_h.length() > 0) 339 { 340 this.thrower = this.worldObj.getPlayerEntityByName(this.field_85053_h); 341 } 342 343 return this.thrower; 344 } 345 }