    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.entity.Entity;
    007import net.minecraft.entity.EntityLiving;
    008import net.minecraft.nbt.NBTTagCompound;
    009import net.minecraft.nbt.NBTTagDouble;
    010import net.minecraft.nbt.NBTTagList;
    011import net.minecraft.util.AxisAlignedBB;
    012import net.minecraft.util.DamageSource;
    013import net.minecraft.util.MathHelper;
    014import net.minecraft.util.MovingObjectPosition;
    015import net.minecraft.util.Vec3;
    016import net.minecraft.world.World;
    017
    018public 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 d1 = this.boundingBox.getAverageEdgeLength() * 4.0D;
    049        d1 *= 64.0D;
    050        return par1 < d1 * d1;
    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 d6 = (double)MathHelper.sqrt_double(par8 * par8 + par10 * par10 + par12 * par12);
    060        this.accelerationX = par8 / d6 * 0.1D;
    061        this.accelerationY = par10 / d6 * 0.1D;
    062        this.accelerationZ = par12 / d6 * 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 d3 = (double)MathHelper.sqrt_double(par3 * par3 + par5 * par5 + par7 * par7);
    078        this.accelerationX = par3 / d3 * 0.1D;
    079        this.accelerationY = par5 / d3 * 0.1D;
    080        this.accelerationZ = par7 / d3 * 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 i = this.worldObj.getBlockId(this.xTile, this.yTile, this.zTile);
    100
    101                if (i == 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 vec3 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ);
    126            Vec3 vec31 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
    127            MovingObjectPosition movingobjectposition = this.worldObj.rayTraceBlocks(vec3, vec31);
    128            vec3 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX, this.posY, this.posZ);
    129            vec31 = this.worldObj.getWorldVec3Pool().getVecFromPool(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
    130
    131            if (movingobjectposition != null)
    132            {
    133                vec31 = this.worldObj.getWorldVec3Pool().getVecFromPool(movingobjectposition.hitVec.xCoord, movingobjectposition.hitVec.yCoord, movingobjectposition.hitVec.zCoord);
    134            }
    135
    136            Entity entity = null;
    137            List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D));
    138            double d0 = 0.0D;
    139
    140            for (int j = 0; j < list.size(); ++j)
    141            {
    142                Entity entity1 = (Entity)list.get(j);
    143
    144                if (entity1.canBeCollidedWith() && (!entity1.isEntityEqual(this.shootingEntity) || this.ticksInAir >= 25))
    145                {
    146                    float f = 0.3F;
    147                    AxisAlignedBB axisalignedbb = entity1.boundingBox.expand((double)f, (double)f, (double)f);
    148                    MovingObjectPosition movingobjectposition1 = axisalignedbb.calculateIntercept(vec3, vec31);
    149
    150                    if (movingobjectposition1 != null)
    151                    {
    152                        double d1 = vec3.distanceTo(movingobjectposition1.hitVec);
    153
    154                        if (d1 < d0 || d0 == 0.0D)
    155                        {
    156                            entity = entity1;
    157                            d0 = d1;
    158                        }
    159                    }
    160                }
    161            }
    162
    163            if (entity != null)
    164            {
    165                movingobjectposition = new MovingObjectPosition(entity);
    166            }
    167
    168            if (movingobjectposition != null)
    169            {
    170                this.onImpact(movingobjectposition);
    171            }
    172
    173            this.posX += this.motionX;
    174            this.posY += this.motionY;
    175            this.posZ += this.motionZ;
    176            float f1 = 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)f1, 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 f2 = this.getMotionFactor();
    202
    203            if (this.isInWater())
    204            {
    205                for (int k = 0; k < 4; ++k)
    206                {
    207                    float f3 = 0.25F;
    208                    this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)f3, this.posY - this.motionY * (double)f3, this.posZ - this.motionZ * (double)f3, this.motionX, this.motionY, this.motionZ);
    209                }
    210
    211                f2 = 0.8F;
    212            }
    213
    214            this.motionX += this.accelerationX;
    215            this.motionY += this.accelerationY;
    216            this.motionZ += this.accelerationZ;
    217            this.motionX *= (double)f2;
    218            this.motionY *= (double)f2;
    219            this.motionZ *= (double)f2;
    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 movingobjectposition);
    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 nbttaglist = par1NBTTagCompound.getTagList("direction");
    265            this.motionX = ((NBTTagDouble)nbttaglist.tagAt(0)).data;
    266            this.motionY = ((NBTTagDouble)nbttaglist.tagAt(1)).data;
    267            this.motionZ = ((NBTTagDouble)nbttaglist.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 vec3 = par1DamageSource.getEntity().getLookVec();
    304
    305                if (vec3 != null)
    306                {
    307                    this.motionX = vec3.xCoord;
    308                    this.motionY = vec3.yCoord;
    309                    this.motionZ = vec3.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}