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