My projectile entity:
public abstract class EntityPlasmaBall extends Entity implements IProjectile
{
private int xTile;
private int yTile;
private int zTile;
private Block inTile;
protected boolean inGround;
public int throwableShake;
/** The entity that threw this throwable item. */
protected EntityLivingBase thrower;
private String throwerName;
private int ticksInGround;
private int ticksInAir;
public Entity ignoreEntity;
private int ignoreTime;
public int powerFactorf;
public EntityPlasmaBall(World worldIn)
{
super(worldIn);
this.xTile = -1;
this.yTile = -1;
this.zTile = -1;
this.setSize(0.75F, 0.75F);
}
protected void entityInit()
{
}
/**
* Checks if the entity is in range to render.
*/
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance)
{
double d0 = this.getEntityBoundingBox().getAverageEdgeLength() * 4.0D;
if (Double.isNaN(d0))
{
d0 = 4.0D;
}
d0 = d0 * 64.0D;
return distance < d0 * d0;
}
public EntityPlasmaBall(World worldIn, double x, double y, double z, int powerfactor)
{
this(worldIn);
this.setPosition(x, y, z);
this.powerFactorf = powerfactor;
}
public EntityPlasmaBall(World worldIn, EntityLivingBase throwerIn, int powerfactor2)
{
this(worldIn, throwerIn.posX, throwerIn.posY + (double)throwerIn.getEyeHeight() - 0.10000000149011612D, throwerIn.posZ, powerfactor2);
this.thrower = throwerIn;
this.powerFactorf = powerfactor2;
}
public void shoot(Entity entityThrower, float rotationPitchIn, float rotationYawIn, float pitchOffset, float velocity, float inaccuracy)
{
float f = -MathHelper.sin(rotationYawIn * 0.017453292F) * MathHelper.cos(rotationPitchIn * 0.017453292F);
float f1 = -MathHelper.sin((rotationPitchIn + pitchOffset) * 0.017453292F);
float f2 = MathHelper.cos(rotationYawIn * 0.017453292F) * MathHelper.cos(rotationPitchIn * 0.017453292F);
this.shoot((double)f, (double)f1, (double)f2, velocity, inaccuracy);
this.motionX += entityThrower.motionX;
this.motionZ += entityThrower.motionZ;
if (!entityThrower.onGround)
{
this.motionY += entityThrower.motionY;
}
}
public void shoot(double x, double y, double z, float velocity, float inaccuracy)
{
float f = MathHelper.sqrt(x * x + y * y + z * z);
x = x / (double)f;
y = y / (double)f;
z = z / (double)f;
x = x + this.rand.nextGaussian() * 0.007499999832361937D * (double)inaccuracy;
y = y + this.rand.nextGaussian() * 0.007499999832361937D * (double)inaccuracy;
z = z + this.rand.nextGaussian() * 0.007499999832361937D * (double)inaccuracy;
x = x * (double)velocity;
y = y * (double)velocity;
z = z * (double)velocity;
this.motionX = x;
this.motionY = y;
this.motionZ = z;
float f1 = MathHelper.sqrt(x * x + z * z);
this.rotationYaw = (float)(MathHelper.atan2(x, z) * (180D / Math.PI));
this.rotationPitch = (float)(MathHelper.atan2(y, (double)f1) * (180D / Math.PI));
this.prevRotationYaw = this.rotationYaw;
this.prevRotationPitch = this.rotationPitch;
this.ticksInGround = 0;
}
@SideOnly(Side.CLIENT)
public void setVelocity(double x, double y, double z)
{
this.motionX = x;
this.motionY = y;
this.motionZ = z;
if (this.prevRotationPitch == 0.0F && this.prevRotationYaw == 0.0F)
{
float f = MathHelper.sqrt(x * x + z * z);
this.rotationYaw = (float)(MathHelper.atan2(x, z) * (180D / Math.PI));
this.rotationPitch = (float)(MathHelper.atan2(y, (double)f) * (180D / Math.PI));
this.prevRotationYaw = this.rotationYaw;
this.prevRotationPitch = this.rotationPitch;
}
}
/**
* Called to update the entity's position/logic.
*/
public void onUpdate()
{
if (this.ticksExisted > 150) {
this.setDead();
return;
}
this.lastTickPosX = this.posX;
this.lastTickPosY = this.posY;
this.lastTickPosZ = this.posZ;
super.onUpdate();
if (this.throwableShake > 0)
{
--this.throwableShake;
}
if (this.inGround)
{
if (this.world.getBlockState(new BlockPos(this.xTile, this.yTile, this.zTile)).getBlock() == this.inTile)
{
++this.ticksInGround;
if (this.ticksInGround == 1200)
{
this.setDead();
}
return;
}
this.inGround = false;
this.motionX *= (double)(this.rand.nextFloat() * 0.2F);
this.motionY *= (double)(this.rand.nextFloat() * 0.2F);
this.motionZ *= (double)(this.rand.nextFloat() * 0.2F);
this.ticksInGround = 0;
this.ticksInAir = 0;
}
else
{
++this.ticksInAir;
}
Vec3d vec3d = new Vec3d(this.posX, this.posY, this.posZ);
Vec3d vec3d1 = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
RayTraceResult raytraceresult = this.world.rayTraceBlocks(vec3d, vec3d1);
vec3d = new Vec3d(this.posX, this.posY, this.posZ);
vec3d1 = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ);
if (raytraceresult != null)
{
vec3d1 = new Vec3d(raytraceresult.hitVec.x, raytraceresult.hitVec.y, raytraceresult.hitVec.z);
}
Entity entity = null;
List<Entity> list = this.world.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().expand(this.motionX, this.motionY, this.motionZ).grow(1.0D));
double d0 = 0.0D;
boolean flag = false;
for (int i = 0; i < list.size(); ++i)
{
Entity entity1 = list.get(i);
if (entity1.canBeCollidedWith())
{
if (entity1 == this.ignoreEntity)
{
flag = true;
}
else if (this.thrower != null && this.ticksExisted < 2 && this.ignoreEntity == null)
{
this.ignoreEntity = entity1;
flag = true;
}
else
{
flag = false;
AxisAlignedBB axisalignedbb = entity1.getEntityBoundingBox().grow(0.30000001192092896D);
RayTraceResult raytraceresult1 = axisalignedbb.calculateIntercept(vec3d, vec3d1);
if (raytraceresult1 != null)
{
double d1 = vec3d.squareDistanceTo(raytraceresult1.hitVec);
if (d1 < d0 || d0 == 0.0D)
{
entity = entity1;
d0 = d1;
}
}
}
}
}
if (this.ignoreEntity != null)
{
if (flag)
{
this.ignoreTime = 2;
}
else if (this.ignoreTime-- <= 0)
{
this.ignoreEntity = null;
}
}
if (entity != null)
{
raytraceresult = new RayTraceResult(entity);
}
if (raytraceresult != null)
{
if (raytraceresult.typeOfHit == RayTraceResult.Type.BLOCK) {
this.onHitBlock(this.getEntityWorld().getBlockState(raytraceresult.getBlockPos()), raytraceresult.getBlockPos());
}
else if (!net.minecraftforge.event.ForgeEventFactory.onProjectileImpact(this, raytraceresult))
{
this.onImpact(raytraceresult);
}
}
this.posX += this.motionX;
this.posY += this.motionY;
this.posZ += this.motionZ;
float f = MathHelper.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
this.rotationYaw = (float)(MathHelper.atan2(this.motionX, this.motionZ) * (180D / Math.PI));
for (this.rotationPitch = (float)(MathHelper.atan2(this.motionY, (double)f) * (180D / Math.PI)); this.rotationPitch - this.prevRotationPitch < -180.0F; this.prevRotationPitch -= 360.0F)
{
;
}
while (this.rotationPitch - this.prevRotationPitch >= 180.0F)
{
this.prevRotationPitch += 360.0F;
}
while (this.rotationYaw - this.prevRotationYaw < -180.0F)
{
this.prevRotationYaw -= 360.0F;
}
while (this.rotationYaw - this.prevRotationYaw >= 180.0F)
{
this.prevRotationYaw += 360.0F;
}
this.rotationPitch = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * 0.2F;
this.rotationYaw = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * 0.2F;
float f1 = 0.99F;
if (this.isInWater())
{
for (int j = 0; j < 4; ++j)
{
float f3 = 0.25F;
this.world.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX - this.motionX * 0.25D, this.posY - this.motionY * 0.25D, this.posZ - this.motionZ * 0.25D, this.motionX, this.motionY, this.motionZ);
}
f1 = 0.8F;
}
this.motionX *= (double)f1;
this.motionY *= (double)f1;
this.motionZ *= (double)f1;
this.setPosition(this.posX, this.posY, this.posZ);
}
/**
* Return the motion factor for this projectile. The factor is multiplied by the original motion.
*/
public abstract void onHitBlock(IBlockState block, BlockPos pos);
/**
* Called when this EntityPlasmaBall hits a block or entity.
*/
protected abstract void onImpact(RayTraceResult result);
public static void registerFixesPlasmaBall(DataFixer fixer, String name)
{
}
/**
* (abstract) Protected helper method to write subclass entity data to NBT.
*/
public void writeEntityToNBT(NBTTagCompound compound)
{
compound.setInteger("xTile", this.xTile);
compound.setInteger("yTile", this.yTile);
compound.setInteger("zTile", this.zTile);
ResourceLocation resourcelocation = Block.REGISTRY.getNameForObject(this.inTile);
compound.setString("inTile", resourcelocation == null ? "" : resourcelocation.toString());
compound.setByte("shake", (byte)this.throwableShake);
compound.setByte("inGround", (byte)(this.inGround ? 1 : 0));
if ((this.throwerName == null || this.throwerName.isEmpty()) && this.thrower instanceof EntityPlayer)
{
this.throwerName = this.thrower.getName();
}
compound.setString("ownerName", this.throwerName == null ? "" : this.throwerName);
}
/**
* (abstract) Protected helper method to read subclass entity data from NBT.
*/
public void readEntityFromNBT(NBTTagCompound compound)
{
this.xTile = compound.getInteger("xTile");
this.yTile = compound.getInteger("yTile");
this.zTile = compound.getInteger("zTile");
if (compound.hasKey("inTile", 8))
{
this.inTile = Block.getBlockFromName(compound.getString("inTile"));
}
else
{
this.inTile = Block.getBlockById(compound.getByte("inTile") & 255);
}
this.throwableShake = compound.getByte("shake") & 255;
this.inGround = compound.getByte("inGround") == 1;
this.thrower = null;
this.throwerName = compound.getString("ownerName");
if (this.throwerName != null && this.throwerName.isEmpty())
{
this.throwerName = null;
}
this.thrower = this.getThrower();
}
/**
* Returns true if other Entities should be prevented from moving through this Entity.
*/
public boolean canBeCollidedWith()
{
return true;
}
public float getCollisionBorderSize()
{
return 1.0F;
}
/**
* Called when the entity is attacked.
*/
@Nullable
public EntityLivingBase getThrower()
{
if (this.thrower == null && this.throwerName != null && !this.throwerName.isEmpty())
{
this.thrower = this.world.getPlayerEntityByName(this.throwerName);
if (this.thrower == null && this.world instanceof WorldServer)
{
try
{
Entity entity = ((WorldServer)this.world).getEntityFromUuid(UUID.fromString(this.throwerName));
if (entity instanceof EntityLivingBase)
{
this.thrower = (EntityLivingBase)entity;
}
}
catch (Throwable var2)
{
this.thrower = null;
}
}
}
return this.thrower;
}
/**
* Gets how bright this entity is.
*/
public float getBrightness()
{
return 1.0F;
}
@SideOnly(Side.CLIENT)
public int getBrightnessForRender()
{
return 15728880;
}
}
public class EntityLargePlasmaBall extends EntityPlasmaBall
{
public double explosionPower = 0.8*powerFactorf;
public int damagefactor =10* powerFactorf;
private static final DataParameter<Boolean> INVULNERABLE = EntityDataManager.<Boolean>createKey(net.minecraft.entity.projectile.EntityWitherSkull.class, DataSerializers.BOOLEAN);
public EntityLargePlasmaBall(World worldIn)
{
super(worldIn);
this.setSize(0.3125F, 0.3125F);
}
public EntityLargePlasmaBall(World worldIn, double x, double y, double z, int powerfactor)
{
super(worldIn, x, y, z, powerfactor);
this.setSize(0.3125F, 0.3125F);
}
public static void registerFixesLargePlasmaBall(DataFixer fixer)
{
EntityPlasmaBall.registerFixesPlasmaBall(fixer, "PlasmaBall");
}
/**
* Return the motion factor for this projectile. The factor is multiplied by the original motion.
*/
@SideOnly(Side.CLIENT)
public EntityLargePlasmaBall(World worldIn, EntityLivingBase throwerIn, int powerfactor2)
{
super(worldIn, throwerIn, powerfactor2);
this.setSize(0.3125F, 0.3125F);
}
/**
* Returns true if the entity is on fire. Used by render to add the fire effect on rendering.
*/
public boolean isBurning()
{
return false;
}
/**
* Explosion resistance of a block relative to this entity
*/
public float getExplosionResistance(Explosion explosionIn, World worldIn, BlockPos pos, IBlockState blockStateIn)
{
float f = super.getExplosionResistance(explosionIn, worldIn, pos, blockStateIn);
Block block = blockStateIn.getBlock();
if (this.isInvulnerable() && block.canEntityDestroy(blockStateIn, worldIn, pos, this) && net.minecraftforge.event.ForgeEventFactory.onEntityDestroyBlock(this.thrower, pos, blockStateIn))
{
f = Math.min(0.8F, f);
}
/*double d=(double)f;
float f1;
if (powerFactorf > 20) {
f1 = (float) (1 * d);
} else
{
f1 =(float) (1 * d);
}
*/
return f;
}
/**
* Called when this EntityFireball hits a block or entity.
*/
protected void onImpact(RayTraceResult result)
{
if (!this.world.isRemote)
{
if (result.entityHit != null)
{
if (powerFactorf >= 20)
{
result.entityHit.attackEntityFrom(DamageSource.DRAGON_BREATH, 50*damagefactor);
result.entityHit.attackEntityFrom(DamageSource.MAGIC, 50*damagefactor);
result.entityHit.attackEntityFrom(DamageSource.LAVA, 50*damagefactor);
} else {
result.entityHit.attackEntityFrom(DamageSource.MAGIC, damagefactor);
result.entityHit.attackEntityFrom(DamageSource.LAVA, damagefactor);
}
if (result.entityHit instanceof EntityLivingBase)
{
if (powerFactorf >= 20)
{
AxisAlignedBB axis = new AxisAlignedBB(this.posX - 5, this.posY - 5, this.posZ - 5,
this.posX + 5, this.posY + 5, this.posZ + 5);
List<EntityLivingBase> targets = getEntityWorld().getEntitiesWithinAABB(EntityLivingBase.class, axis);
for (EntityLivingBase mob : targets) {
(mob).setHealth((mob).getHealth() - damagefactor);
mob.hurtResistantTime = -1;
}
}
((EntityLivingBase)result.entityHit).setFire(30);
}
if (result.entityHit instanceof EntityPlayer)
{
if (powerFactorf >= 20)
{
AxisAlignedBB axis = new AxisAlignedBB(this.posX - 5, this.posY - 5, this.posZ - 5,
this.posX + 5, this.posY + 5, this.posZ + 5);
List<EntityLivingBase> targets = getEntityWorld().getEntitiesWithinAABB(EntityLivingBase.class, axis);
for (EntityLivingBase mob : targets) {
(mob).setHealth((mob).getHealth() - damagefactor);
mob.hurtResistantTime = -1;
}
}
((EntityLivingBase)result.entityHit).setFire(30);
}
this.world.newExplosion(this, this.posX, this.posY, this.posZ, (float)this.explosionPower, false, this.world.getGameRules().getBoolean("mobGriefing"));
this.setDead();
}
}
}
/**
* Returns true if other Entities should be prevented from moving through this Entity.
*/
public boolean canBeCollidedWith()
{
return false;
}
@Override
@ParametersAreNonnullByDefault
public void onHitBlock(IBlockState hitBlock, BlockPos pos)
{
if (!hitBlock.getMaterial().isSolid()) {
// Go through non solid block
return;
} else {
if (powerFactorf >= 51){
getEntityWorld().destroyBlock(pos, false);
}
}
this.world.newExplosion(this, this.posX, this.posY, this.posZ, (float)this.explosionPower, false, this.world.getGameRules().getBoolean("mobGriefing"));
this.setDead();
}
/**
* Called when the entity is attacked.
*/
public boolean attackEntityFrom(DamageSource source, float amount)
{
return false;
}
protected void entityInit()
{
this.dataManager.register(INVULNERABLE, Boolean.valueOf(false));
}
/**
* Return whether this skull comes from an invulnerable (aura) wither boss.
*/
public boolean isInvulnerable()
{
return ((Boolean)this.dataManager.get(INVULNERABLE)).booleanValue();
}
/**
* Set whether this skull comes from an invulnerable (aura) wither boss.
*/
public void setInvulnerable(boolean invulnerable)
{
this.dataManager.set(INVULNERABLE, Boolean.valueOf(invulnerable));
}
protected boolean isFireballFiery()
{
return false;
}
}
And my rendering codes:
public class RenderLargePlasmaBall extends Render<EntityLargePlasmaBall>
{
private static final ResourceLocation INVULNERABLE_WITHER_TEXTURES = new ResourceLocation("textures/entity/wither/wither_invulnerable.png");
private static final ResourceLocation WITHER_TEXTURES = new ResourceLocation("textures/entity/wither/wither.png");
/** The Skeleton's head model. */
private final ModelSkeletonHead skeletonHeadModel = new ModelSkeletonHead();
public RenderLargePlasmaBall(RenderManager renderManagerIn)
{
super(renderManagerIn);
}
private float getRenderYaw(float p_82400_1_, float p_82400_2_, float p_82400_3_)
{
float f;
for (f = p_82400_2_ - p_82400_1_; f < -180.0F; f += 360.0F)
{
;
}
while (f >= 180.0F)
{
f -= 360.0F;
}
return p_82400_1_ + p_82400_3_ * f;
}
/**
* Renders the desired {@code T} type Entity.
*/
public void doRender(EntityLargePlasmaBall entity, double x, double y, double z, float entityYaw, float partialTicks)
{
GlStateManager.pushMatrix();
GlStateManager.disableCull();
float f = this.getRenderYaw(entity.prevRotationYaw, entity.rotationYaw, partialTicks);
float f1 = entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * partialTicks;
GlStateManager.translate((float)x, (float)y, (float)z);
float f2 = 0.0625F;
GlStateManager.enableRescaleNormal();
GlStateManager.scale(-1.0F, -1.0F, 1.0F);
GlStateManager.enableAlpha();
this.bindEntityTexture(entity);
if (this.renderOutlines)
{
GlStateManager.enableColorMaterial();
GlStateManager.enableOutlineMode(this.getTeamColor(entity));
}
this.skeletonHeadModel.render(entity, 0.0F, 0.0F, 0.0F, f, f1, 0.0625F);
if (this.renderOutlines)
{
GlStateManager.disableOutlineMode();
GlStateManager.disableColorMaterial();
}
GlStateManager.popMatrix();
super.doRender(entity, x, y, z, entityYaw, partialTicks);
}
/**
* Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture.
*/
protected ResourceLocation getEntityTexture(EntityLargePlasmaBall entity)
{
return entity.isInvulnerable() ? INVULNERABLE_WITHER_TEXTURES : WITHER_TEXTURES;
}
}