Spell hierarchisation and refactoring, plus miscellaneous renaming and minor refactoring

- Adds new generic spell superclasses which aim to group spells such that duplicate code is eliminated, and functions standardised, as much as possible.
- Removes numerous spell classes which are no longer required as a result of this change, and replaces them with instances of the relevant generic spell classes.
- Changes the remaining spell classes to fit with the new system, mostly by having them extend the generic spell classes.
- Completes the transfer of particle spawning to the ParticleBuilder system.
This commit is contained in:
Electroblob
2018-06-20 22:25:54 +01:00
parent b6400c72a7
commit 29261082f1
222 changed files with 4412 additions and 9016 deletions
@@ -1,6 +1,5 @@
package electroblob.wizardry.entity.construct;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.projectile.EntityTippedArrow;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
@@ -8,15 +7,8 @@ import net.minecraft.world.World;
public class EntityArrowRain extends EntityMagicConstruct {
public EntityArrowRain(World par1World){
super(par1World);
this.height = 3.0f;
this.width = 5.0f;
}
public EntityArrowRain(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
public EntityArrowRain(World world){
super(world);
this.height = 3.0f;
this.width = 5.0f;
}
@@ -12,8 +12,9 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.server.SPacketEntityVelocity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
public class EntityBlackHole extends EntityMagicConstruct {
@@ -34,21 +35,6 @@ public class EntityBlackHole extends EntityMagicConstruct {
}
}
public EntityBlackHole(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.width = 6.0f;
this.height = 3.0f;
randomiser = new int[30];
for(int i = 0; i < randomiser.length; i++){
randomiser[i] = this.rand.nextInt(10);
}
randomiser2 = new int[30];
for(int i = 0; i < randomiser2.length; i++){
randomiser2[i] = this.rand.nextInt(10);
}
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
@@ -135,11 +121,10 @@ public class EntityBlackHole extends EntityMagicConstruct {
}
}
}
/**
* Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD
*/
public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d){
@Override
@SideOnly(Side.CLIENT)
public boolean isInRangeToRenderDist(double distance){
return true;
}
@@ -16,15 +16,8 @@ import net.minecraft.world.World;
public class EntityBlizzard extends EntityMagicConstruct {
public EntityBlizzard(World par1World){
super(par1World);
this.height = 1.0f;
this.width = 1.0f;
}
public EntityBlizzard(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
public EntityBlizzard(World world){
super(world);
this.height = 1.0f;
this.width = 1.0f;
}
@@ -28,13 +28,6 @@ public class EntityBubble extends EntityMagicConstruct {
super(world);
}
public EntityBubble(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
boolean isDarkOrb, float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
// this.setSize(0.1f, 0.1f);
this.isDarkOrb = isDarkOrb;
}
@Override
public double getMountedYOffset(){
return 0.1;
@@ -10,23 +10,14 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.SoundEvents;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class EntityDecay extends EntityMagicConstruct {
public int textureIndex = 0;
public static final int LIFETIME = 400;
public EntityDecay(World par1World){
super(par1World);
textureIndex = this.rand.nextInt(10);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityDecay(World par1World, double x, double y, double z, EntityLivingBase caster){
super(par1World, x, y, z, caster, LIFETIME, 1);
public EntityDecay(World world){
super(world);
textureIndex = this.rand.nextInt(10);
this.height = 0.2f;
this.width = 2.0f;
@@ -37,7 +28,7 @@ public class EntityDecay extends EntityMagicConstruct {
super.onUpdate();
if(this.rand.nextInt(700) == 0 && this.ticksExisted + 100 < LIFETIME)
if(this.rand.nextInt(700) == 0 && this.ticksExisted + 100 < lifetime)
this.playSound(SoundEvents.BLOCK_LAVA_AMBIENT, 0.2F + rand.nextFloat() * 0.2F,
0.6F + rand.nextFloat() * 0.15F);
@@ -50,7 +41,7 @@ public class EntityDecay extends EntityMagicConstruct {
// damaged each tick.
// In this case, we do want particles to be shown.
if(!target.isPotionActive(WizardryPotions.decay))
target.addPotionEffect(new PotionEffect(WizardryPotions.decay, LIFETIME, 0));
target.addPotionEffect(new PotionEffect(WizardryPotions.decay, lifetime, 0));
}
}
@@ -67,23 +58,15 @@ public class EntityDecay extends EntityMagicConstruct {
}
}
protected void entityInit(){
}
@Override protected void entityInit(){}
@Override protected void readEntityFromNBT(NBTTagCompound nbttagcompound){}
@Override protected void writeEntityToNBT(NBTTagCompound nbttagcompound){}
@Override
protected void readEntityFromNBT(NBTTagCompound nbttagcompound){
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbttagcompound){
}
/**
* Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD
*/
public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d){
public boolean isInRangeToRenderDist(double distance){
return true;
}
}
@@ -7,6 +7,7 @@ import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityFallingBlock;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.MobEffects;
import net.minecraft.network.play.server.SPacketEntityVelocity;
@@ -22,13 +23,6 @@ public class EntityEarthquake extends EntityMagicConstruct {
this.width = 1.0f;
}
public EntityEarthquake(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 1.0f;
this.width = 1.0f;
}
public void onUpdate(){
super.onUpdate();
@@ -99,19 +93,16 @@ public class EntityEarthquake extends EntityMagicConstruct {
}
}
}
// TODO: Uncomment once 2.1.0 is released
// }else{
//
// // Constant 15 blocks for now
// List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(15, this.posX, this.posY,
// this.posZ, world, EntityPlayer.class);
//
// float magnitude = 6f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime;
//
// // Makes the screen shake
// for(EntityLivingBase target : targets){
// target.setAngles(0, this.ticksExisted % 4 < 2 ? magnitude : -magnitude);
// }
}else{
// Constant 15 blocks for now
List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(15, posX, posY, posZ, world, EntityPlayer.class);
float magnitude = 6f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime;
// Makes the screen shake
for(EntityPlayer target : targets){
target.rotationPitch = this.ticksExisted % 2 == 0 ? magnitude : -magnitude;
}
}
}
@@ -12,15 +12,8 @@ import net.minecraft.world.World;
public class EntityFireRing extends EntityMagicConstruct {
public EntityFireRing(World par1World){
super(par1World);
this.height = 1.0f;
this.width = 5.0f;
}
public EntityFireRing(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
public EntityFireRing(World world){
super(world);
this.height = 1.0f;
this.width = 5.0f;
}
@@ -13,34 +13,20 @@ import net.minecraft.world.World;
public class EntityFireSigil extends EntityMagicConstruct {
public EntityFireSigil(World par1World){
super(par1World);
public EntityFireSigil(World world){
super(world);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityFireSigil(World par1World, double x, double y, double z, EntityLivingBase caster,
float damageMultiplier){
super(par1World, x, y, z, caster, -1, damageMultiplier);
this.height = 0.2f;
this.width = 2.0f;
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9){
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
@Override
public void onUpdate(){
super.onUpdate();
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY,
this.posZ, this.world);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(width/2, posX, posY, posZ, world);
for(EntityLivingBase target : targets){
@@ -76,13 +62,9 @@ public class EntityFireSigil extends EntityMagicConstruct {
}
@Override
protected void entityInit(){
protected void entityInit(){}
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire(){
return false;
}
@@ -20,40 +20,34 @@ public class EntityForcefield extends EntityMagicConstruct {
super(world);
this.height = 6;
this.width = 6;
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3,
this.posY + 3, this.posZ + 3));
}
public EntityForcefield(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){
// y-3 because it needs to be centred on the given position
// Damage multiplier is 1 because forcefields do no damage!
super(world, x, y - 3, z, caster, lifetime, 1.0f);
this.height = 6;
this.width = 6;
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3,
this.posY + 3, this.posZ + 3));
this.setEntityBoundingBox(new AxisAlignedBB(posX - 3, posY - 3, posZ - 3, posX + 3, posY + 3, posZ + 3));
}
@Override
public boolean canBeCollidedWith(){
return !this.isDead;
}
public AxisAlignedBB getCollisionBox(Entity par1Entity){
return par1Entity.getEntityBoundingBox();
@Override
public AxisAlignedBB getCollisionBox(Entity entity){
return entity.getEntityBoundingBox();
}
@Override
public void onUpdate(){
super.onUpdate();
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.5, this.posX, this.posY + 3,
this.posZ, this.world);
// TESTME: This used to say posY+3, but I'm pretty sure that's wrong because of how the bounding box was set...
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(3.5, posX, posY, posZ, world);
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
double multiplier = (3.5 - target.getDistance(this.posX, this.posY + 3, this.posZ)) * 0.1;
double multiplier = (3.5 - target.getDistance(this.posX, this.posY, this.posZ)) * 0.1;
target.addVelocity((target.posX - this.posX) * multiplier,
(target.posY - (this.posY + 3)) * multiplier, (target.posZ - this.posZ) * multiplier);
(target.posY - this.posY) * multiplier, (target.posZ - this.posZ) * multiplier);
// Player motion is handled on that player's client so needs packets
if(target instanceof EntityPlayerMP){
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
@@ -78,19 +72,18 @@ public class EntityForcefield extends EntityMagicConstruct {
}
}
public boolean attackEntityFrom(DamageSource source, float par2){
@Override
public boolean attackEntityFrom(DamageSource source, float damage){
if(source != null && source.getImmediateSource() != null){
// Now works for any source of damage.
source.getImmediateSource().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f);
}
super.attackEntityFrom(source, par2);
super.attackEntityFrom(source, damage);
return false;
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire(){
return false;
}
@@ -16,26 +16,13 @@ import net.minecraft.world.World;
public class EntityFrostSigil extends EntityMagicConstruct {
public EntityFrostSigil(World par1World){
super(par1World);
public EntityFrostSigil(World world){
super(world);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityFrostSigil(World par1World, double x, double y, double z, EntityLivingBase caster,
float damageMultiplier){
super(par1World, x, y, z, caster, -1, damageMultiplier);
this.height = 0.2f;
this.width = 2.0f;
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9){
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
@Override
public void onUpdate(){
super.onUpdate();
@@ -48,20 +35,11 @@ public class EntityFrostSigil extends EntityMagicConstruct {
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
double velX = target.motionX;
double velY = target.motionY;
double velZ = target.motionZ;
target.attackEntityFrom(this.getCaster() != null
WizardryUtilities.attackEntityWithoutKnockback(target, this.getCaster() != null
? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FROST)
: DamageSource.MAGIC, 8);
// Removes knockback
target.motionX = velX;
target.motionY = velY;
target.motionZ = velZ;
if(!MagicDamage.isEntityImmune(DamageType.FROST, target))
target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 1));
@@ -82,13 +60,9 @@ public class EntityFrostSigil extends EntityMagicConstruct {
}
@Override
protected void entityInit(){
protected void entityInit(){}
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire(){
return false;
}
@@ -1,20 +1,12 @@
package electroblob.wizardry.entity.construct;
import electroblob.wizardry.entity.projectile.EntityIceShard;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.world.World;
public class EntityHailstorm extends EntityMagicConstruct {
public EntityHailstorm(World par1World){
super(par1World);
this.height = 3.0f;
this.width = 5.0f;
}
public EntityHailstorm(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
public EntityHailstorm(World world){
super(world);
this.height = 3.0f;
this.width = 5.0f;
}
@@ -25,12 +17,13 @@ public class EntityHailstorm extends EntityMagicConstruct {
if(!this.world.isRemote){
// System.out.println(this.rotationYaw);
EntityIceShard iceshard = new EntityIceShard(world, this.posX + rand.nextDouble() * 6 - 3,
this.posY + rand.nextDouble() * 4 - 2, this.posZ + rand.nextDouble() * 6 - 3);
EntityIceShard iceshard = new EntityIceShard(world);
iceshard.setPosition(this.posX + rand.nextDouble() * 6 - 3, this.posY + rand.nextDouble() * 4 - 2,
this.posZ + rand.nextDouble() * 6 - 3);
iceshard.motionX = Math.cos(Math.toRadians(this.rotationYaw + 90));
iceshard.motionY = -0.6;
iceshard.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90));
iceshard.setShootingEntity(this.getCaster());
iceshard.setCaster(this.getCaster());
iceshard.damageMultiplier = this.damageMultiplier;
this.world.spawnEntity(iceshard);
}
@@ -28,15 +28,8 @@ public class EntityHammer extends EntityMagicConstruct {
/** How long the hammer has been falling for. */
public int fallTime;
public EntityHammer(World par1World){
super(par1World);
this.setSize(1.0f, 1.9F);
this.noClip = false;
}
public EntityHammer(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
public EntityHammer(World world){
super(world);
this.setSize(1.0f, 1.9F);
this.motionX = 0.0D;
this.motionY = 0.0D;
@@ -20,13 +20,7 @@ public class EntityHealAura extends EntityMagicConstruct {
this.width = 5.0f;
}
public EntityHealAura(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 1.0f;
this.width = 5.0f;
}
@Override
public void onUpdate(){
if(this.ticksExisted % 25 == 1){
@@ -37,8 +31,7 @@ public class EntityHealAura extends EntityMagicConstruct {
if(!this.world.isRemote){
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(2.5d, this.posX, this.posY,
this.posZ, this.world);
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(2.5, posX, posY, posZ, world);
for(EntityLivingBase target : targets){
@@ -83,9 +76,7 @@ public class EntityHealAura extends EntityMagicConstruct {
}
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire(){
return false;
}
@@ -16,12 +16,7 @@ public class EntityIceSpike extends EntityMagicConstruct {
this.setSize(0.5f, 1.0f);
}
public EntityIceSpike(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.setSize(0.5f, 1.0f);
}
@Override
public void onUpdate(){
if(lifetime - this.ticksExisted < 15){
@@ -1,8 +1,8 @@
package electroblob.wizardry.entity.construct;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.world.World;
@Deprecated // This needs changing into a particle
public class EntityLightningPulse extends EntityMagicConstruct {
public EntityLightningPulse(World world){
@@ -10,15 +10,7 @@ public class EntityLightningPulse extends EntityMagicConstruct {
this.setSize(6, 0.2f);
}
public EntityLightningPulse(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.setSize(6, 0.2f);
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire(){
return false;
}
@@ -16,26 +16,13 @@ import net.minecraft.world.World;
public class EntityLightningSigil extends EntityMagicConstruct {
public EntityLightningSigil(World par1World){
super(par1World);
public EntityLightningSigil(World world){
super(world);
this.height = 0.2f;
this.width = 2.0f;
}
public EntityLightningSigil(World par1World, double x, double y, double z, EntityLivingBase caster,
float damageMultiplier){
super(par1World, x, y, z, caster, -1, damageMultiplier);
this.height = 0.2f;
this.width = 2.0f;
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9){
this.setPosition(par1, par3, par5);
this.setRotation(par7, par8);
}
@Override
public void onUpdate(){
super.onUpdate();
@@ -56,10 +43,8 @@ public class EntityLightningSigil extends EntityMagicConstruct {
double velZ = target.motionZ;
// Only works if target is actually damaged to account for hurtResistantTime
if(target.attackEntityFrom(
getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK)
: DamageSource.MAGIC,
6)){
if(target.attackEntityFrom(getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, getCaster(),
DamageType.SHOCK) : DamageSource.MAGIC, 6)){
// Removes knockback
target.motionX = velX;
@@ -125,13 +110,9 @@ public class EntityLightningSigil extends EntityMagicConstruct {
}
@Override
protected void entityInit(){
protected void entityInit(){}
}
/**
* Return whether this entity should be rendered as on fire.
*/
@Override
public boolean canRenderOnFire(){
return false;
}
@@ -10,6 +10,8 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
/**
* This class is for all inanimate magical constructs which are not projectiles. It was made from scratch to provide a
@@ -42,30 +44,18 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi
/** The damage multiplier for this construct, determined by the wand with which it was cast. */
public float damageMultiplier = 1.0f;
public EntityMagicConstruct(World par1World){
super(par1World);
this.height = 1.0f;
this.width = 1.0f;
this.noClip = true;
}
public EntityMagicConstruct(World world, double x, double y, double z, EntityLivingBase caster, int lifetime,
float damageMultiplier){
public EntityMagicConstruct(World world){
super(world);
this.height = 1.0f;
this.width = 1.0f;
this.setPosition(x, y, z);
this.caster = new WeakReference<EntityLivingBase>(caster);
this.noClip = true;
this.lifetime = lifetime;
this.damageMultiplier = damageMultiplier;
}
// Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow
// it to stick in blocks.
@Override
public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch,
int posRotationIncrements, boolean teleport){
@SideOnly(Side.CLIENT)
public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport){
this.setPosition(x, y, z);
this.setRotation(yaw, pitch);
}
@@ -136,6 +126,10 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi
public EntityLivingBase getCaster(){
return caster == null ? null : caster.get();
}
public void setCaster(EntityLivingBase caster){
this.caster = new WeakReference<EntityLivingBase>(caster);
}
/**
* Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this construct as the
@@ -34,17 +34,13 @@ public class EntityTornado extends EntityMagicConstruct {
this.width = 5.0f;
this.isImmuneToFire = false;
}
public EntityTornado(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, double velX,
double velZ, float damageMultiplier){
super(world, x, y, z, caster, lifetime, damageMultiplier);
this.height = 8.0f;
this.width = 5.0f;
public void setHorizontalVelocity(double velX, double velZ){
this.velX = velX;
this.velZ = velZ;
this.isImmuneToFire = false;
}
@Override
public void onUpdate(){
super.onUpdate();