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
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        protected EntityLiving thrower;
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 var15 = Vec3.getVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ);
169            Vec3 var2 = Vec3.getVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
170            MovingObjectPosition var3 = this.worldObj.rayTraceBlocks(var15, var2);
171            var15 = Vec3.getVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ);
172            var2 = Vec3.getVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
173    
174            if (var3 != null)
175            {
176                var2 = Vec3.getVec3Pool().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                Iterator var8 = var5.iterator();
185    
186                while (var8.hasNext())
187                {
188                    Entity var9 = (Entity)var8.next();
189    
190                    if (var9.canBeCollidedWith() && (var9 != this.thrower || this.ticksInAir >= 5))
191                    {
192                        float var10 = 0.3F;
193                        AxisAlignedBB var11 = var9.boundingBox.expand((double)var10, (double)var10, (double)var10);
194                        MovingObjectPosition var12 = var11.calculateIntercept(var15, var2);
195    
196                        if (var12 != null)
197                        {
198                            double var13 = var15.distanceTo(var12.hitVec);
199    
200                            if (var13 < var6 || var6 == 0.0D)
201                            {
202                                var4 = var9;
203                                var6 = var13;
204                            }
205                        }
206                    }
207                }
208    
209                if (var4 != null)
210                {
211                    var3 = new MovingObjectPosition(var4);
212                }
213            }
214    
215            if (var3 != null)
216            {
217                this.onImpact(var3);
218            }
219    
220            this.posX += this.motionX;
221            this.posY += this.motionY;
222            this.posZ += this.motionZ;
223            float var16 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
224            this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI);
225    
226            for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)var16) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F)
227            {
228                ;
229            }
230    
231            while (this.rotationPitch - this.prevRotationPitch >= 180.0F)
232            {
233                this.prevRotationPitch += 360.0F;
234            }
235    
236            while (this.rotationYaw - this.prevRotationYaw < -180.0F)
237            {
238                this.prevRotationYaw -= 360.0F;
239            }
240    
241            while (this.rotationYaw - this.prevRotationYaw >= 180.0F)
242            {
243                this.prevRotationYaw += 360.0F;
244            }
245    
246            this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
247            this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;
248            float var17 = 0.99F;
249            float var18 = this.getGravityVelocity();
250    
251            if (this.isInWater())
252            {
253                for (int var7 = 0; var7 < 4; ++var7)
254                {
255                    float var19 = 0.25F;
256                    this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)var19, this.posY - this.motionY * (double)var19, this.posZ - this.motionZ * (double)var19, this.motionX, this.motionY, this.motionZ);
257                }
258    
259                var17 = 0.8F;
260            }
261    
262            this.motionX *= (double)var17;
263            this.motionY *= (double)var17;
264            this.motionZ *= (double)var17;
265            this.motionY -= (double)var18;
266            this.setPosition(this.posX, this.posY, this.posZ);
267        }
268    
269        /**
270         * Gets the amount of gravity to apply to the thrown entity with each tick.
271         */
272        protected float getGravityVelocity()
273        {
274            return 0.03F;
275        }
276    
277        /**
278         * Called when this EntityThrowable hits a block or entity.
279         */
280        protected abstract void onImpact(MovingObjectPosition var1);
281    
282        /**
283         * (abstract) Protected helper method to write subclass entity data to NBT.
284         */
285        public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
286        {
287            par1NBTTagCompound.setShort("xTile", (short)this.xTile);
288            par1NBTTagCompound.setShort("yTile", (short)this.yTile);
289            par1NBTTagCompound.setShort("zTile", (short)this.zTile);
290            par1NBTTagCompound.setByte("inTile", (byte)this.inTile);
291            par1NBTTagCompound.setByte("shake", (byte)this.throwableShake);
292            par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0));
293        }
294    
295        /**
296         * (abstract) Protected helper method to read subclass entity data from NBT.
297         */
298        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
299        {
300            this.xTile = par1NBTTagCompound.getShort("xTile");
301            this.yTile = par1NBTTagCompound.getShort("yTile");
302            this.zTile = par1NBTTagCompound.getShort("zTile");
303            this.inTile = par1NBTTagCompound.getByte("inTile") & 255;
304            this.throwableShake = par1NBTTagCompound.getByte("shake") & 255;
305            this.inGround = par1NBTTagCompound.getByte("inGround") == 1;
306        }
307    
308        @SideOnly(Side.CLIENT)
309        public float getShadowSize()
310        {
311            return 0.0F;
312        }
313    }