package electroblob.wizardry.entity.projectile; import electroblob.wizardry.Wizardry; import electroblob.wizardry.registry.Spells; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.util.BlockUtils; import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; import electroblob.wizardry.util.ParticleBuilder; import io.netty.buffer.ByteBuf; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntityGhast; import net.minecraft.entity.projectile.EntitySmallFireball; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.DamageSource; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; /** * It's a fireball - but unlike vanilla fireballs, it actually looks like a fireball, and isn't completely useless for * attacking things (acceleration from stationary? Really, Mojang? No wonder I had so many blaze rods back in the day...) */ @Mod.EventBusSubscriber public class EntityMagicFireball extends EntityMagicProjectile { protected static final int ACCELERATION_CONVERSION_FACTOR = 10; /** The damage dealt by this fireball. If this is -1, the damage for the fireball spell will be used instead; * this is for when the fireball is not from a spell (i.e. a vanilla fireball replacement). */ protected float damage = -1; /** The number of seconds entities are set on fire by this fireball. If this is -1, the damage for the fireball * spell will be used instead; this is for when the fireball is not from a spell (i.e. a vanilla fireball replacement). */ protected int burnDuration = -1; /** The lifetime of this fireball in ticks. This needs to be stored so that it can be changed for vanilla replacements, * or mobs that shoot fireballs would have severely reduced range! */ protected int lifetime = 16; public EntityMagicFireball(World world){ super(world); this.setSize(0.5f, 0.5f); } public void setDamage(float damage){ this.damage = damage; } public void setBurnDuration(int burnDuration){ this.burnDuration = burnDuration; } public float getDamage(){ // I'm lazy, I'd rather not have an entire fireball spell class just to set two fields on the entity return damage == -1 ? Spells.fireball.getProperty(Spell.DAMAGE).floatValue() : damage; } public int getBurnDuration(){ return burnDuration == -1 ? Spells.fireball.getProperty(Spell.BURN_DURATION).intValue() : burnDuration; } @Override protected void onImpact(RayTraceResult rayTrace){ if(!world.isRemote){ Entity entityHit = rayTrace.entityHit; if(entityHit != null){ float damage = getDamage() * damageMultiplier; DamageSource source = getDamageSource(entityHit); entityHit.attackEntityFrom(source, damage); if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit) && getBurnDuration() > 0) entityHit.setFire(getBurnDuration()); }else{ BlockPos pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); // Remember that canPlaceBlock should ALWAYS be the last thing that gets checked, or it risks other mods // thinking the block was placed even when a later condition prevents it, which may have side-effects if(this.world.isAirBlock(pos) && BlockUtils.canPlaceBlock(thrower, world, pos)){ this.world.setBlockState(pos, Blocks.FIRE.getDefaultState()); } } //this.playSound(WizardrySounds.ENTITY_MAGIC_FIREBALL_HIT, 2, 0.8f + rand.nextFloat() * 0.3f); this.setDead(); } } protected DamageSource getDamageSource(Entity entityHit){ return MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(); } @Override public void onUpdate(){ super.onUpdate(); if(world.isRemote){ for(int i=0; i<5; i++){ double dx = (rand.nextDouble() - 0.5) * width; double dy = (rand.nextDouble() - 0.5) * height + this.height/2 - 0.1; // -0.1 because flames aren't centred double dz = (rand.nextDouble() - 0.5) * width; double v = 0.06; ParticleBuilder.create(ParticleBuilder.Type.MAGIC_FIRE) .pos(this.getPositionVector().add(dx - this.motionX/2, dy, dz - this.motionZ/2)) .vel(-v * dx, -v * dy, -v * dz).scale(width*2).time(10).spawn(world); if(ticksExisted > 1){ dx = (rand.nextDouble() - 0.5) * width; dy = (rand.nextDouble() - 0.5) * height + this.height / 2 - 0.1; dz = (rand.nextDouble() - 0.5) * width; ParticleBuilder.create(ParticleBuilder.Type.MAGIC_FIRE) .pos(this.getPositionVector().add(dx - this.motionX, dy, dz - this.motionZ)) .vel(-v * dx, -v * dy, -v * dz).scale(width*2).time(10).spawn(world); } } } } @Override public boolean canBeCollidedWith(){ return true; } @Override public float getCollisionBorderSize(){ return 1.0F; } @Override public boolean attackEntityFrom(DamageSource source, float amount){ if(this.isEntityInvulnerable(source)){ return false; }else{ this.markVelocityChanged(); if(source.getTrueSource() != null){ Vec3d vec3d = source.getTrueSource().getLookVec(); if(vec3d != null){ double speed = MathHelper.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ); this.motionX = vec3d.x * speed; this.motionY = vec3d.y * speed; this.motionZ = vec3d.z * speed; this.lifetime = 160; } if(source.getTrueSource() instanceof EntityLivingBase){ this.setCaster((EntityLivingBase)source.getTrueSource()); } return true; }else{ return false; } } } public void setLifetime(int lifetime){ this.lifetime = lifetime; } @Override public int getLifetime(){ return lifetime; } @Override public boolean hasNoGravity(){ return true; } @Override public boolean canRenderOnFire(){ return false; } @Override public void writeSpawnData(ByteBuf buffer){ buffer.writeInt(lifetime); super.writeSpawnData(buffer); } @Override public void readSpawnData(ByteBuf buffer){ lifetime = buffer.readInt(); super.readSpawnData(buffer); } @Override public void readEntityFromNBT(NBTTagCompound nbttagcompound){ super.readEntityFromNBT(nbttagcompound); lifetime = nbttagcompound.getInteger("lifetime"); } @Override public void writeEntityToNBT(NBTTagCompound nbttagcompound){ super.writeEntityToNBT(nbttagcompound); nbttagcompound.setInteger("lifetime", lifetime); } @SubscribeEvent public static void onEntityJoinWorldEvent(EntityJoinWorldEvent event){ // Replaces all vanilla fireballs with wizardry ones if(Wizardry.settings.replaceVanillaFireballs && event.getEntity() instanceof EntitySmallFireball){ event.setCanceled(true); EntityMagicFireball fireball = new EntityMagicFireball(event.getWorld()); fireball.thrower = ((EntitySmallFireball)event.getEntity()).shootingEntity; fireball.setPosition(event.getEntity().posX, event.getEntity().posY, event.getEntity().posZ); fireball.setDamage(5); fireball.setBurnDuration(5); fireball.setLifetime(40); fireball.motionX = ((EntitySmallFireball)event.getEntity()).accelerationX * ACCELERATION_CONVERSION_FACTOR; fireball.motionY = ((EntitySmallFireball)event.getEntity()).accelerationY * ACCELERATION_CONVERSION_FACTOR; fireball.motionZ = ((EntitySmallFireball)event.getEntity()).accelerationZ * ACCELERATION_CONVERSION_FACTOR; event.getWorld().spawnEntity(fireball); } } }