    001package net.minecraft.entity.projectile;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.util.List;
    006import net.minecraft.block.Block;
    007import net.minecraft.entity.Entity;
    008import net.minecraft.entity.EntityLiving;
    009import net.minecraft.entity.IProjectile;
    010import net.minecraft.entity.player.EntityPlayer;
    011import net.minecraft.nbt.NBTTagCompound;
    012import net.minecraft.util.AxisAlignedBB;
    013import net.minecraft.util.EnumMovingObjectType;
    014import net.minecraft.util.MathHelper;
    015import net.minecraft.util.MovingObjectPosition;
    016import net.minecraft.util.Vec3;
    017import net.minecraft.world.World;
    018
    019public abstract class EntityThrowable extends Entity implements IProjectile
    020{
    021    private int xTile = -1;
    022    private int yTile = -1;
    023    private int zTile = -1;
    024    private int inTile = 0;
    025    protected boolean inGround = false;
    026    public int throwableShake = 0;
    027
    028    /**
    029     * Is the entity that throws this 'thing' (snowball, ender pearl, eye of ender or potion)
    030     */
    031    private EntityLiving thrower;
    032    private String throwerName = null;
    033    private int ticksInGround;
    034    private int ticksInAir = 0;
    035
    036    public EntityThrowable(World par1World)
    037    {
    038        super(par1World);
    039        this.setSize(0.25F, 0.25F);
    040    }
    041
    042    protected void entityInit() {}
    043
    044    @SideOnly(Side.CLIENT)
    045
    046    /**
    047     * Checks if the entity is in range to render by using the past in distance and comparing it to its average edge
    048     * length * 64 * renderDistanceWeight Args: distance
    049     */
    050    public boolean isInRangeToRenderDist(double par1)
    051    {
    052        double d1 = this.boundingBox.getAverageEdgeLength() * 4.0D;
    053        d1 *= 64.0D;
    054        return par1 < d1 * d1;
    055    }
    056
    057    public EntityThrowable(World par1World, EntityLiving par2EntityLiving)
    058    {
    059        super(par1World);
    060        this.thrower = par2EntityLiving;
    061        this.setSize(0.25F, 0.25F);
    062        this.setLocationAndAngles(par2EntityLiving.posX, par2EntityLiving.posY + (double)par2EntityLiving.getEyeHeight(), par2EntityLiving.posZ, par2EntityLiving.rotationYaw, par2EntityLiving.rotationPitch);
    063        this.posX -= (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F);
    064        this.posY -= 0.10000000149011612D;
    065        this.posZ -= (double)(MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F);
    066        this.setPosition(this.posX, this.posY, this.posZ);
    067        this.yOffset = 0.0F;
    068        float f = 0.4F;
    069        this.motionX = (double)(-MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI) * f);
    070        this.motionZ = (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI) * f);
    071        this.motionY = (double)(-MathHelper.sin((this.rotationPitch + this.func_70183_g()) / 180.0F * (float)Math.PI) * f);
    072        this.setThrowableHeading(this.motionX, this.motionY, this.motionZ, this.func_70182_d(), 1.0F);
    073    }
    074
    075    public EntityThrowable(World par1World, double par2, double par4, double par6)
    076    {
    077        super(par1World);
    078        this.ticksInGround = 0;
    079        this.setSize(0.25F, 0.25F);
    080        this.setPosition(par2, par4, par6);
    081        this.yOffset = 0.0F;
    082    }
    083
    084    protected float func_70182_d()
    085    {
    086        return 1.5F;
    087    }
    088
    089    protected float func_70183_g()
    090    {
    091        return 0.0F;
    092    }
    093
    094    /**
    095     * Similar to setArrowHeading, it's point the throwable entity to a x, y, z direction.
    096     */
    097    public void setThrowableHeading(double par1, double par3, double par5, float par7, float par8)
    098    {
    099        float f2 = MathHelper.sqrt_double(par1 * par1 + par3 * par3 + par5 * par5);
    100        par1 /= (double)f2;
    101        par3 /= (double)f2;
    102        par5 /= (double)f2;
    103        par1 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8;
    104        par3 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8;
    105        par5 += this.rand.nextGaussian() * 0.007499999832361937D * (double)par8;
    106        par1 *= (double)par7;
    107        par3 *= (double)par7;
    108        par5 *= (double)par7;
    109        this.motionX = par1;
    110        this.motionY = par3;
    111        this.motionZ = par5;
    112        float f3 = MathHelper.sqrt_double(par1 * par1 + par5 * par5);
    113        this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI);
    114        this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)f3) * 180.0D / Math.PI);
    115        this.ticksInGround = 0;
    116    }
    117
    118    @SideOnly(Side.CLIENT)
    119
    120    /**
    121     * Sets the velocity to the args. Args: x, y, z
    122     */
    123    public void setVelocity(double par1, double par3, double par5)
    124    {
    125        this.motionX = par1;
    126        this.motionY = par3;
    127        this.motionZ = par5;
    128
    129        if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F)
    130        {
    131            float f = MathHelper.sqrt_double(par1 * par1 + par5 * par5);
    132            this.prevRotationYaw = this.rotationYaw = (float)(Math.atan2(par1, par5) * 180.0D / Math.PI);
    133            this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(par3, (double)f) * 180.0D / Math.PI);
    134        }
    135    }
    136
    137    /**
    138     * Called to update the entity's position/logic.
    139     */
    140    public void onUpdate()
    141    {
    142        this.lastTickPosX = this.posX;
    143        this.lastTickPosY = this.posY;
    144        this.lastTickPosZ = this.posZ;
    145        super.onUpdate();
    146
    147        if (this.throwableShake > 0)
    148        {
    149            --this.throwableShake;
    150        }
    151
    152        if (this.inGround)
    153        {
    154            int i = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile);
    155
    156            if (i == this.inTile)
    157            {
    158                ++this.ticksInGround;
    159
    160                if (this.ticksInGround == 1200)
    161                {
    162                    this.setDead();
    163                }
    164
    165                return;
    166            }
    167
    168            this.inGround = false;
    169            this.motionX *= (double)(this.rand.nextFloat() * 0.2F);
    170            this.motionY *= (double)(this.rand.nextFloat() * 0.2F);
    171            this.motionZ *= (double)(this.rand.nextFloat() * 0.2F);
    172            this.ticksInGround = 0;
    173            this.ticksInAir = 0;
    174        }
    175        else
    176        {
    177            ++this.ticksInAir;
    178        }
    179
    180        Vec3 vec3 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ);
    181        Vec3 vec31 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
    182        MovingObjectPosition movingobjectposition = this.worldObj.rayTraceBlocks(vec3, vec31);
    183        vec3 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ);
    184        vec31 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
    185
    186        if (movingobjectposition != null)
    187        {
    188            vec31 = this.worldObj.getWorldVec3Pool().getVecFromPool(movingobjectposition.hitVec.xCoord, movingobjectposition.hitVec.yCoord, movingobjectposition.hitVec.zCoord);
    189        }
    190
    191        if (!this.worldObj.isRemote)
    192        {
    193            Entity entity = null;
    194            List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));
    195            double d0 = 0.0D;
    196            EntityLiving entityliving = this.getThrower();
    197
    198            for (int j = 0; j < list.size(); ++j)
    199            {
    200                Entity entity1 = (Entity)list.get(j);
    201
    202                if (entity1.canBeCollidedWith() && (entity1 != entityliving || this.ticksInAir >= 5))
    203                {
    204                    float f = 0.3F;
    205                    AxisAlignedBB axisalignedbb = entity1.boundingBox.expand((double)f, (double)f, (double)f);
    206                    MovingObjectPosition movingobjectposition1 = axisalignedbb.calculateIntercept(vec3, vec31);
    207
    208                    if (movingobjectposition1 != null)
    209                    {
    210                        double d1 = vec3.distanceTo(movingobjectposition1.hitVec);
    211
    212                        if (d1 < d0 || d0 == 0.0D)
    213                        {
    214                            entity = entity1;
    215                            d0 = d1;
    216                        }
    217                    }
    218                }
    219            }
    220
    221            if (entity != null)
    222            {
    223                movingobjectposition = new MovingObjectPosition(entity);
    224            }
    225        }
    226
    227        if (movingobjectposition != null)
    228        {
    229            if (movingobjectposition.typeOfHit == EnumMovingObjectType.TILE && this.worldObj.getBlockId(movingobjectposition.blockX, movingobjectposition.blockY, movingobjectposition.blockZ) == Block.portal.blockID)
    230            {
    231                this.setInPortal();
    232            }
    233            else
    234            {
    235                this.onImpact(movingobjectposition);
    236            }
    237        }
    238
    239        this.posX += this.motionX;
    240        this.posY += this.motionY;
    241        this.posZ += this.motionZ;
    242        float f1 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionZ * this.motionZ);
    243        this.rotationYaw = (float)(Math.atan2(this.motionX, this.motionZ) * 180.0D / Math.PI);
    244
    245        for (this.rotationPitch = (float)(Math.atan2(this.motionY, (double)f1) * 180.0D / Math.PI); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F)
    246        {
    247            ;
    248        }
    249
    250        while (this.rotationPitch - this.prevRotationPitch >= 180.0F)
    251        {
    252            this.prevRotationPitch += 360.0F;
    253        }
    254
    255        while (this.rotationYaw - this.prevRotationYaw < -180.0F)
    256        {
    257            this.prevRotationYaw -= 360.0F;
    258        }
    259
    260        while (this.rotationYaw - this.prevRotationYaw >= 180.0F)
    261        {
    262            this.prevRotationYaw += 360.0F;
    263        }
    264
    265        this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
    266        this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;
    267        float f2 = 0.99F;
    268        float f3 = this.getGravityVelocity();
    269
    270        if (this.isInWater())
    271        {
    272            for (int k = 0; k < 4; ++k)
    273            {
    274                float f4 = 0.25F;
    275                this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)f4, this.posY - this.motionY * (double)f4, this.posZ - this.motionZ * (double)f4, this.motionX, this.motionY, this.motionZ);
    276            }
    277
    278            f2 = 0.8F;
    279        }
    280
    281        this.motionX *= (double)f2;
    282        this.motionY *= (double)f2;
    283        this.motionZ *= (double)f2;
    284        this.motionY -= (double)f3;
    285        this.setPosition(this.posX, this.posY, this.posZ);
    286    }
    287
    288    /**
    289     * Gets the amount of gravity to apply to the thrown entity with each tick.
    290     */
    291    protected float getGravityVelocity()
    292    {
    293        return 0.03F;
    294    }
    295
    296    /**
    297     * Called when this EntityThrowable hits a block or entity.
    298     */
    299    protected abstract void onImpact(MovingObjectPosition movingobjectposition);
    300
    301    /**
    302     * (abstract) Protected helper method to write subclass entity data to NBT.
    303     */
    304    public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
    305    {
    306        par1NBTTagCompound.setShort("xTile", (short)this.xTile);
    307        par1NBTTagCompound.setShort("yTile", (short)this.yTile);
    308        par1NBTTagCompound.setShort("zTile", (short)this.zTile);
    309        par1NBTTagCompound.setByte("inTile", (byte)this.inTile);
    310        par1NBTTagCompound.setByte("shake", (byte)this.throwableShake);
    311        par1NBTTagCompound.setByte("inGround", (byte)(this.inGround ? 1 : 0));
    312
    313        if ((this.throwerName == null || this.throwerName.length() == 0) && this.thrower != null && this.thrower instanceof EntityPlayer)
    314        {
    315            this.throwerName = this.thrower.getEntityName();
    316        }
    317
    318        par1NBTTagCompound.setString("ownerName", this.throwerName == null ? "" : this.throwerName);
    319    }
    320
    321    /**
    322     * (abstract) Protected helper method to read subclass entity data from NBT.
    323     */
    324    public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
    325    {
    326        this.xTile = par1NBTTagCompound.getShort("xTile");
    327        this.yTile = par1NBTTagCompound.getShort("yTile");
    328        this.zTile = par1NBTTagCompound.getShort("zTile");
    329        this.inTile = par1NBTTagCompound.getByte("inTile") & 255;
    330        this.throwableShake = par1NBTTagCompound.getByte("shake") & 255;
    331        this.inGround = par1NBTTagCompound.getByte("inGround") == 1;
    332        this.throwerName = par1NBTTagCompound.getString("ownerName");
    333
    334        if (this.throwerName != null && this.throwerName.length() == 0)
    335        {
    336            this.throwerName = null;
    337        }
    338    }
    339
    340    @SideOnly(Side.CLIENT)
    341    public float getShadowSize()
    342    {
    343        return 0.0F;
    344    }
    345
    346    public EntityLiving getThrower()
    347    {
    348        if (this.thrower == null && this.throwerName != null && this.throwerName.length() > 0)
    349        {
    350            this.thrower = this.worldObj.getPlayerEntityByName(this.throwerName);
    351        }
    352
    353        return this.thrower;
    354    }
    355}