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    }