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 }