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 implements IProjectile
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 = 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(var15, var2);
171            var15 = 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                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                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 var16 = 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)var16) * 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 var17 = 0.99F;
256            float var18 = this.getGravityVelocity();
257    
258            if (this.isInWater())
259            {
260                for (int var7 = 0; var7 < 4; ++var7)
261                {
262                    float var19 = 0.25F;
263                    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);
264                }
265    
266                var17 = 0.8F;
267            }
268    
269            this.motionX *= (double)var17;
270            this.motionY *= (double)var17;
271            this.motionZ *= (double)var17;
272            this.motionY -= (double)var18;
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    
302        /**
303         * (abstract) Protected helper method to read subclass entity data from NBT.
304         */
305        public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
306        {
307            this.xTile = par1NBTTagCompound.getShort("xTile");
308            this.yTile = par1NBTTagCompound.getShort("yTile");
309            this.zTile = par1NBTTagCompound.getShort("zTile");
310            this.inTile = par1NBTTagCompound.getByte("inTile") & 255;
311            this.throwableShake = par1NBTTagCompound.getByte("shake") & 255;
312            this.inGround = par1NBTTagCompound.getByte("inGround") == 1;
313        }
314    
315        @SideOnly(Side.CLIENT)
316        public float getShadowSize()
317        {
318            return 0.0F;
319        }
320    }