001 package net.minecraft.entity.projectile; 002 003 import cpw.mods.fml.relauncher.Side; 004 import cpw.mods.fml.relauncher.SideOnly; 005 import java.util.List; 006 import net.minecraft.entity.Entity; 007 import net.minecraft.entity.EntityLiving; 008 import net.minecraft.nbt.NBTTagCompound; 009 import net.minecraft.nbt.NBTTagDouble; 010 import net.minecraft.nbt.NBTTagList; 011 import net.minecraft.util.AxisAlignedBB; 012 import net.minecraft.util.DamageSource; 013 import net.minecraft.util.MathHelper; 014 import net.minecraft.util.MovingObjectPosition; 015 import net.minecraft.util.Vec3; 016 import net.minecraft.world.World; 017 018 public abstract class EntityFireball extends Entity 019 { 020 private int xTile = -1; 021 private int yTile = -1; 022 private int zTile = -1; 023 private int inTile = 0; 024 private boolean inGround = false; 025 public EntityLiving shootingEntity; 026 private int ticksAlive; 027 private int ticksInAir = 0; 028 public double accelerationX; 029 public double accelerationY; 030 public double accelerationZ; 031 032 public EntityFireball(World par1World) 033 { 034 super(par1World); 035 this.setSize(1.0F, 1.0F); 036 } 037 038 protected void entityInit() {} 039 040 @SideOnly(Side.CLIENT) 041 042 /** 043 * Checks if the entity is in range to render by using the past in distance and comparing it to its average edge 044 * length * 64 * renderDistanceWeight Args: distance 045 */ 046 public boolean isInRangeToRenderDist(double par1) 047 { 048 double var3 = this.boundingBox.getAverageEdgeLength() * 4.0D; 049 var3 *= 64.0D; 050 return par1 < var3 * var3; 051 } 052 053 public EntityFireball(World par1World, double par2, double par4, double par6, double par8, double par10, double par12) 054 { 055 super(par1World); 056 this.setSize(1.0F, 1.0F); 057 this.setLocationAndAngles(par2, par4, par6, this.rotationYaw, this.rotationPitch); 058 this.setPosition(par2, par4, par6); 059 double var14 = (double)MathHelper.sqrt_double(par8 * par8 + par10 * par10 + par12 * par12); 060 this.accelerationX = par8 / var14 * 0.1D; 061 this.accelerationY = par10 / var14 * 0.1D; 062 this.accelerationZ = par12 / var14 * 0.1D; 063 } 064 065 public EntityFireball(World par1World, EntityLiving par2EntityLiving, double par3, double par5, double par7) 066 { 067 super(par1World); 068 this.shootingEntity = par2EntityLiving; 069 this.setSize(1.0F, 1.0F); 070 this.setLocationAndAngles(par2EntityLiving.posX, par2EntityLiving.posY, par2EntityLiving.posZ, par2EntityLiving.rotationYaw, par2EntityLiving.rotationPitch); 071 this.setPosition(this.posX, this.posY, this.posZ); 072 this.yOffset = 0.0F; 073 this.motionX = this.motionY = this.motionZ = 0.0D; 074 par3 += this.rand.nextGaussian() * 0.4D; 075 par5 += this.rand.nextGaussian() * 0.4D; 076 par7 += this.rand.nextGaussian() * 0.4D; 077 double var9 = (double)MathHelper.sqrt_double(par3 * par3 + par5 * par5 + par7 * par7); 078 this.accelerationX = par3 / var9 * 0.1D; 079 this.accelerationY = par5 / var9 * 0.1D; 080 this.accelerationZ = par7 / var9 * 0.1D; 081 } 082 083 /** 084 * Called to update the entity's position/logic. 085 */ 086 public void onUpdate() 087 { 088 if (!this.worldObj.isRemote && (this.shootingEntity != null && this.shootingEntity.isDead || !this.worldObj.blockExists((int)this.posX, (int)this.posY, (int)this.posZ))) 089 { 090 this.setDead(); 091 } 092 else 093 { 094 super.onUpdate(); 095 this.setFire(1); 096 097 if (this.inGround) 098 { 099 int var1 = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile); 100 101 if (var1 == this.inTile) 102 { 103 ++this.ticksAlive; 104 105 if (this.ticksAlive == 600) 106 { 107 this.setDead(); 108 } 109 110 return; 111 } 112 113 this.inGround = false; 114 this.motionX *= (double)(this.rand.nextFloat() * 0.2F); 115 this.motionY *= (double)(this.rand.nextFloat() * 0.2F); 116 this.motionZ *= (double)(this.rand.nextFloat() * 0.2F); 117 this.ticksAlive = 0; 118 this.ticksInAir = 0; 119 } 120 else 121 { 122 ++this.ticksInAir; 123 } 124 125 Vec3 var15 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 126 Vec3 var2 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 127 MovingObjectPosition var3 = this.worldObj.rayTraceBlocks(var15, var2); 128 var15 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ); 129 var2 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); 130 131 if (var3 != null) 132 { 133 var2 = this.worldObj.getWorldVec3Pool().getVecFromPool(var3.hitVec.xCoord, var3.hitVec.yCoord, var3.hitVec.zCoord); 134 } 135 136 Entity var4 = null; 137 List var5 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D)); 138 double var6 = 0.0D; 139 140 for (int var8 = 0; var8 < var5.size(); ++var8) 141 { 142 Entity var9 = (Entity)var5.get(var8); 143 144 if (var9.canBeCollidedWith() && (!var9.isEntityEqual(this.shootingEntity) || this.ticksInAir >= 25)) 145 { 146 float var10 = 0.3F; 147 AxisAlignedBB var11 = var9.boundingBox.expand((double)var10, (double)var10, (double)var10); 148 MovingObjectPosition var12 = var11.calculateIntercept(var15, var2); 149 150 if (var12 != null) 151 { 152 double var13 = var15.distanceTo(var12.hitVec); 153 154 if (var13 < var6 || var6 == 0.0D) 155 { 156 var4 = var9; 157 var6 = var13; 158 } 159 } 160 } 161 } 162 163 if (var4 != null) 164 { 165 var3 = new MovingObjectPosition(var4); 166 } 167 168 if (var3 != null) 169 { 170 this.onImpact(var3); 171 } 172 173 this.posX += this.motionX; 174 this.posY += this.motionY; 175 this.posZ += this.motionZ; 176 float var16 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ); 177 this.rotationYaw = (float)(Math.atan2(this.motionZ, this.motionX) * 180.0D / Math.PI) + 90.0F; 178 179 for (this.rotationPitch = (float)(Math.atan2((double)var16, this.motionY) * 180.0D / Math.PI) - 90.0F; this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F) 180 { 181 ; 182 } 183 184 while (this.rotationPitch - this.prevRotationPitch >= 180.0F) 185 { 186 this.prevRotationPitch += 360.0F; 187 } 188 189 while (this.rotationYaw - this.prevRotationYaw < -180.0F) 190 { 191 this.prevRotationYaw -= 360.0F; 192 } 193 194 while (this.rotationYaw - this.prevRotationYaw >= 180.0F) 195 { 196 this.prevRotationYaw += 360.0F; 197 } 198 199 this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F; 200 this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F; 201 float var17 = this.getMotionFactor(); 202 203 if (this.isInWater()) 204 { 205 for (int var19 = 0; var19 < 4; ++var19) 206 { 207 float var18 = 0.25F; 208 this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)var18, this.posY - this.motionY * (double)var18, this.posZ - this.motionZ * (double)var18, this.motionX, this.motionY, this.motionZ); 209 } 210 211 var17 = 0.8F; 212 } 213 214 this.motionX += this.accelerationX; 215 this.motionY += this.accelerationY; 216 this.motionZ += this.accelerationZ; 217 this.motionX *= (double)var17; 218 this.motionY *= (double)var17; 219 this.motionZ *= (double)var17; 220 this.worldObj.spawnParticle("smoke", this.posX, this.posY + 0.5D, this.posZ, 0.0D, 0.0D, 0.0D); 221 this.setPosition(this.posX, this.posY, this.posZ); 222 } 223 } 224 225 /** 226 * Return the motion factor for this projectile. The factor is multiplied by the original motion. 227 */ 228 protected float getMotionFactor() 229 { 230 return 0.95F; 231 } 232 233 /** 234 * Called when this EntityFireball hits a block or entity. 235 */ 236 protected abstract void onImpact(MovingObjectPosition var1); 237 238 /** 239 * (abstract) Protected helper method to write subclass entity data to NBT. 240 */ 241 public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) 242 { 243 par1NBTTagCompound.setShort("xTile", (short)this.xTile); 244 par1NBTTagCompound.setShort("yTile", (short)this.yTile); 245 par1NBTTagCompound.setShort("zTile", (short)this.zTile); 246 par1NBTTagCompound.setByte("inTile", (byte)this.inTile); 247 par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0)); 248 par1NBTTagCompound.setTag("direction", this.newDoubleNBTList(new double[] {this.motionX, this.motionY, this.motionZ})); 249 } 250 251 /** 252 * (abstract) Protected helper method to read subclass entity data from NBT. 253 */ 254 public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) 255 { 256 this.xTile = par1NBTTagCompound.getShort("xTile"); 257 this.yTile = par1NBTTagCompound.getShort("yTile"); 258 this.zTile = par1NBTTagCompound.getShort("zTile"); 259 this.inTile = par1NBTTagCompound.getByte("inTile") & 255; 260 this.inGround = par1NBTTagCompound.getByte("inGround") == 1; 261 262 if (par1NBTTagCompound.hasKey("direction")) 263 { 264 NBTTagList var2 = par1NBTTagCompound.getTagList("direction"); 265 this.motionX = ((NBTTagDouble)var2.tagAt(0)).data; 266 this.motionY = ((NBTTagDouble)var2.tagAt(1)).data; 267 this.motionZ = ((NBTTagDouble)var2.tagAt(2)).data; 268 } 269 else 270 { 271 this.setDead(); 272 } 273 } 274 275 /** 276 * Returns true if other Entities should be prevented from moving through this Entity. 277 */ 278 public boolean canBeCollidedWith() 279 { 280 return true; 281 } 282 283 public float getCollisionBorderSize() 284 { 285 return 1.0F; 286 } 287 288 /** 289 * Called when the entity is attacked. 290 */ 291 public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) 292 { 293 if (this.isEntityInvulnerable()) 294 { 295 return false; 296 } 297 else 298 { 299 this.setBeenAttacked(); 300 301 if (par1DamageSource.getEntity() != null) 302 { 303 Vec3 var3 = par1DamageSource.getEntity().getLookVec(); 304 305 if (var3 != null) 306 { 307 this.motionX = var3.xCoord; 308 this.motionY = var3.yCoord; 309 this.motionZ = var3.zCoord; 310 this.accelerationX = this.motionX * 0.1D; 311 this.accelerationY = this.motionY * 0.1D; 312 this.accelerationZ = this.motionZ * 0.1D; 313 } 314 315 if (par1DamageSource.getEntity() instanceof EntityLiving) 316 { 317 this.shootingEntity = (EntityLiving)par1DamageSource.getEntity(); 318 } 319 320 return true; 321 } 322 else 323 { 324 return false; 325 } 326 } 327 } 328 329 @SideOnly(Side.CLIENT) 330 public float getShadowSize() 331 { 332 return 0.0F; 333 } 334 335 /** 336 * Gets how bright this entity is. 337 */ 338 public float getBrightness(float par1) 339 { 340 return 1.0F; 341 } 342 343 @SideOnly(Side.CLIENT) 344 public int getBrightnessForRender(float par1) 345 { 346 return 15728880; 347 } 348 }