diff --git a/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java b/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java index 01928e33..6ff9b08e 100644 --- a/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java +++ b/src/main/java/electroblob/wizardry/spell/EmpoweringPresence.java @@ -2,68 +2,55 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.entity.living.ISpellCaster; import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import java.util.List; +import javax.annotation.Nullable; @Mod.EventBusSubscriber -public class EmpoweringPresence extends Spell { +public class EmpoweringPresence extends SpellAreaEffect { public EmpoweringPresence(){ super("empowering_presence", SpellActions.POINT_UP, false); - addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); + this.alwaysSucceed(true); + this.targetAllies(true); + addProperties(EFFECT_DURATION, EFFECT_STRENGTH); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - List targets = EntityUtils.getEntitiesWithinRadius(getProperty(EFFECT_RADIUS).doubleValue() - * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); + if(target instanceof EntityPlayer || target instanceof ISpellCaster){ // Only useful for spell casters - for(EntityPlayer target : targets){ - if(AllyDesignationSystem.isPlayerAlly(caster, target) || target == caster){ + int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)); - int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)); - - target.addPotionEffect(new PotionEffect(WizardryPotions.empowerment, - (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), - getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier)); - } + target.addPotionEffect(new PotionEffect(WizardryPotions.empowerment, + (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), + getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier)); } - if(world.isRemote){ - - for(int i = 0; i < 50 * modifiers.get(WizardryItems.blast_upgrade); i++){ - - double radius = (1 + world.rand.nextDouble() * 4) * modifiers.get(WizardryItems.blast_upgrade); - float angle = world.rand.nextFloat() * (float)Math.PI * 2; - - double x = caster.posX + radius * MathHelper.cos(angle); - double y = caster.getEntityBoundingBox().minY; - double z = caster.posZ + radius * MathHelper.sin(angle); - - ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50).clr(0.5f, 0.4f, 0.75f).spawn(world); - - } - } - - playSound(world, caster, ticksInUse, -1, modifiers); return true; } + @Override + protected void spawnParticle(World world, double x, double y, double z){ + ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50).clr(0.5f, 0.4f, 0.75f).spawn(world); + } + @Override protected String getTranslationKey(){ return Wizardry.tisTheSeason ? super.getTranslationKey() + "_festive" : super.getTranslationKey(); diff --git a/src/main/java/electroblob/wizardry/spell/FontOfMana.java b/src/main/java/electroblob/wizardry/spell/FontOfMana.java index fdb2a977..04c53db7 100644 --- a/src/main/java/electroblob/wizardry/spell/FontOfMana.java +++ b/src/main/java/electroblob/wizardry/spell/FontOfMana.java @@ -4,65 +4,49 @@ import electroblob.wizardry.event.SpellCastEvent; import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import java.util.List; +import javax.annotation.Nullable; -public class FontOfMana extends Spell { +public class FontOfMana extends SpellAreaEffect { public FontOfMana(){ super("font_of_mana", SpellActions.POINT_UP, false); this.soundValues(0.7f, 1.2f, 0.4f); - addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); + this.particleDensity(1.25f); + this.targetAllies(true); + this.alwaysSucceed(true); + addProperties(EFFECT_DURATION, EFFECT_STRENGTH); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - double maxRadius = getProperty(EFFECT_RADIUS).doubleValue(); - - List targets = EntityUtils.getEntitiesWithinRadius( - maxRadius * modifiers.get(WizardryItems.blast_upgrade), - caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); - - for(EntityPlayer target : targets){ - if(AllyDesignationSystem.isPlayerAlly(caster, target) || target == caster){ - target.addPotionEffect(new PotionEffect(WizardryPotions.font_of_mana, - (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), - (int)(getProperty(EFFECT_STRENGTH).intValue() + (modifiers.get(SpellModifiers.POTENCY) - 1) * 2))); - } + if(target instanceof EntityPlayer){ // Font of mana is only useful to players + target.addPotionEffect(new PotionEffect(WizardryPotions.font_of_mana, + (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), + (int)(getProperty(EFFECT_STRENGTH).intValue() + (modifiers.get(SpellModifiers.POTENCY) - 1) * 2))); } - if(world.isRemote){ - for(int i = 0; i < 100 * modifiers.get(WizardryItems.blast_upgrade); i++){ - - double radius = (1 + world.rand.nextDouble() * (maxRadius - 1)) * modifiers.get(WizardryItems.blast_upgrade); - float angle = world.rand.nextFloat() * (float)Math.PI * 2; - ; - float hue = world.rand.nextFloat() * 0.4f; - - double x = caster.posX + radius * MathHelper.cos(angle); - double y = caster.getEntityBoundingBox().minY; - double z = caster.posZ + radius * MathHelper.sin(angle); - - ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50) - .clr(1, 1 - hue, 0.6f + hue).spawn(world); - } - } - - playSound(world, caster, ticksInUse, -1, modifiers); - return true; } + @Override + protected void spawnParticle(World world, double x, double y, double z){ + float hue = world.rand.nextFloat() * 0.4f; + ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50) + .clr(1, 1 - hue, 0.6f + hue).spawn(world); + } + @SubscribeEvent(priority = EventPriority.LOW) // Doesn't really matter but there's no point processing it if casting is blocked public static void onSpellCastPreEvent(SpellCastEvent.Pre event){ // Moved from ItemWand (quite why this wasn't done with modifiers before I don't know!) diff --git a/src/main/java/electroblob/wizardry/spell/ForestsCurse.java b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java index ec6c6dbc..1eeb2e5e 100644 --- a/src/main/java/electroblob/wizardry/spell/ForestsCurse.java +++ b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java @@ -8,23 +8,28 @@ import electroblob.wizardry.util.ParticleBuilder.Type; import net.minecraft.entity.EntityLivingBase; import net.minecraft.init.MobEffects; import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import javax.annotation.Nullable; + public class ForestsCurse extends SpellAreaEffect { public ForestsCurse(){ - super("forests_curse", SpellActions.POINT_UP); + super("forests_curse", SpellActions.POINT_UP, false); + this.alwaysSucceed(true); this.soundValues(1, 1.1f, 0.2f); addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); } @Override - protected void affectEntity(World world, EntityLivingBase caster, EntityLivingBase target, SpellModifiers modifiers){ + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ if(!MagicDamage.isEntityImmune(DamageType.POISON, target) && EntityUtils.isLiving(target)){ - - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), - getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY)); + + DamageSource source = caster != null ? MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON) : DamageSource.MAGIC; + target.attackEntityFrom(source, getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY)); int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)); int duration = (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)); @@ -34,6 +39,8 @@ public class ForestsCurse extends SpellAreaEffect { target.addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, duration, amplifier)); target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, duration, amplifier)); } + + return true; } @Override diff --git a/src/main/java/electroblob/wizardry/spell/GroupHeal.java b/src/main/java/electroblob/wizardry/spell/GroupHeal.java index 3c498ca4..a93e9404 100644 --- a/src/main/java/electroblob/wizardry/spell/GroupHeal.java +++ b/src/main/java/electroblob/wizardry/spell/GroupHeal.java @@ -1,47 +1,41 @@ package electroblob.wizardry.spell; import electroblob.wizardry.item.SpellActions; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.ParticleBuilder; +import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.EnumHand; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import java.util.List; +import javax.annotation.Nullable; -public class GroupHeal extends Spell { +public class GroupHeal extends SpellAreaEffect { public GroupHeal(){ super("group_heal", SpellActions.POINT_UP, false); this.soundValues(0.7f, 1.2f, 0.4f); - addProperties(EFFECT_RADIUS, HEALTH); + this.targetAllies(true); + addProperties(HEALTH); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - boolean flag = false; + if(target.getHealth() < target.getMaxHealth() && target.getHealth() > 0){ - List targets = EntityUtils.getLivingWithinRadius(getProperty(EFFECT_RADIUS).floatValue() - * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + Heal.heal(target, getProperty(HEALTH).floatValue() * modifiers.get(SpellModifiers.POTENCY)); - for(EntityLivingBase target : targets){ - - if(target == caster || AllyDesignationSystem.isAllied(caster, target)){ - - if(target.getHealth() < target.getMaxHealth() && target.getHealth() > 0){ - - Heal.heal(target, getProperty(HEALTH).floatValue() * modifiers.get(SpellModifiers.POTENCY)); - - if(world.isRemote) ParticleBuilder.spawnHealParticles(world, target); - playSound(world, target, ticksInUse, -1, modifiers); - flag = true; - } - } + if(world.isRemote) ParticleBuilder.spawnHealParticles(world, target); + playSound(world, target, ticksInUse, -1, modifiers); + return true; } - return flag; + return false; // Only succeeds if something was healed + } + + @Override + protected void spawnParticleEffect(World world, Vec3d origin, double radius, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + // We're spawning particles above so don't bother with this method } } diff --git a/src/main/java/electroblob/wizardry/spell/IceAge.java b/src/main/java/electroblob/wizardry/spell/IceAge.java index 4d40da67..fe760597 100644 --- a/src/main/java/electroblob/wizardry/spell/IceAge.java +++ b/src/main/java/electroblob/wizardry/spell/IceAge.java @@ -6,8 +6,11 @@ import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.BlockUtils; +import electroblob.wizardry.util.EntityUtils; +import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; +import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -15,55 +18,90 @@ import net.minecraft.potion.PotionEffect; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import java.util.List; +import javax.annotation.Nullable; -public class IceAge extends Spell { +public class IceAge extends SpellAreaEffect { public static final String FREEZE_DURATION = "freeze_duration"; public IceAge(){ super("ice_age", SpellActions.POINT_DOWN, false); this.soundValues(1.5f, 1.0f, 0); - addProperties(EFFECT_RADIUS, FREEZE_DURATION, EFFECT_DURATION, EFFECT_STRENGTH); - } - - @Override - public boolean requiresPacket(){ - return false; + this.alwaysSucceed(true); + addProperties(FREEZE_DURATION, EFFECT_DURATION, EFFECT_STRENGTH); } @Override public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + freezeNearbyBlocks(world, new Vec3d(caster.posX, caster.getEntityBoundingBox().minY, caster.posZ), caster, modifiers); + return super.cast(world, caster, hand, ticksInUse, modifiers); + } - float radius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade); + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + freezeNearbyBlocks(world, caster.getPositionVector(), caster, modifiers); + return super.cast(world, caster, hand, ticksInUse, target, modifiers); + } - List targets = EntityUtils.getLivingWithinRadius(radius, caster.posX, caster.posY, caster.posZ, world); + @Override + public boolean cast(World world, double x, double y, double z, EnumFacing direction, int ticksInUse, int duration, SpellModifiers modifiers){ + freezeNearbyBlocks(world, new Vec3d(x, y, z), null, modifiers); + return super.cast(world, x, y, z, direction, ticksInUse, duration, modifiers); + } - for(EntityLivingBase target : targets){ - if(AllyDesignationSystem.isValidTarget(caster, target)){ - if(!world.isRemote){ + @Override + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - if(target instanceof EntityLiving){ - if(((BlockStatue)WizardryBlocks.ice_statue).convertToStatue((EntityLiving)target, - (int)(getProperty(FREEZE_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)))){ - target.playSound(WizardrySounds.MISC_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); - } - }else if(target instanceof EntityPlayer){ - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, - (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), - getProperty(EFFECT_STRENGTH).intValue())); - } - } + if(target instanceof EntityLiving){ + if(((BlockStatue)WizardryBlocks.ice_statue).convertToStatue((EntityLiving)target, + (int)(getProperty(FREEZE_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)))){ + target.playSound(WizardrySounds.MISC_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); } + }else if(target instanceof EntityPlayer){ + target.addPotionEffect(new PotionEffect(WizardryPotions.frost, + (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), + getProperty(EFFECT_STRENGTH).intValue())); } + return true; + } + + @Override + protected void spawnParticleEffect(World world, Vec3d origin, double radius, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + + for(int i=0; i<100; i++){ + double speed = (world.rand.nextBoolean() ? 1 : -1) * (0.1 + 0.05 * world.rand.nextDouble()); + ParticleBuilder.create(Type.SNOW) + .pos(origin.x, origin.y + world.rand.nextDouble() * 3, origin.z) + .vel(0, 0, 0) + .scale(2) + .spin(world.rand.nextDouble() * (radius - 0.5) + 0.5, speed) + .spawn(world); + } + + for(int i=0; i<60; i++){ + double speed = (world.rand.nextBoolean() ? 1 : -1) * (0.05 + 0.02 * world.rand.nextDouble()); + ParticleBuilder.create(Type.CLOUD) + .pos(origin.x, origin.y + world.rand.nextDouble() * 2.5, origin.z) + .clr(0xffffff) + .spin(world.rand.nextDouble() * (radius - 1) + 0.5, speed) + .spawn(world); + } + } + + private void freezeNearbyBlocks(World world, Vec3d origin, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + if(!world.isRemote && EntityUtils.canDamageBlocks(caster, world)){ + + double radius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade); + for(int i = -(int)radius; i <= (int)radius; i++){ for(int j = -(int)radius; j <= (int)radius; j++){ - BlockPos pos = new BlockPos(caster).add(i, 0, j); + BlockPos pos = new BlockPos(origin).add(i, 0, j); Integer y = BlockUtils.getNearestSurface(world, new BlockPos(pos), EnumFacing.UP, (int)radius, true, BlockUtils.SurfaceCriteria.SOLID_LIQUID_TO_AIR); @@ -71,7 +109,7 @@ public class IceAge extends Spell { pos = new BlockPos(pos.getX(), y, pos.getZ()); - double dist = caster.getDistance(caster.posX + i, y, caster.posZ + j); + double dist = origin.distanceTo(new Vec3d(origin.x + i, y, origin.z + j)); // Randomised with weighting so that the nearer the block the more likely it is to be snowed. if(y != -1 && world.rand.nextInt((int)(dist * 2) + 1) < radius && dist < radius){ @@ -81,31 +119,6 @@ public class IceAge extends Spell { } } } - - if(world.isRemote){ - - for(int i=0; i<100; i++){ - double speed = (world.rand.nextBoolean() ? 1 : -1) * (0.1 + 0.05 * world.rand.nextDouble()); - ParticleBuilder.create(Type.SNOW) - .pos(caster.posX, caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 3, caster.posZ) - .vel(0, 0, 0) - .scale(2) - .spin(world.rand.nextDouble() * (radius - 0.5) + 0.5, speed) - .spawn(world); - } - - for(int i=0; i<60; i++){ - double speed = (world.rand.nextBoolean() ? 1 : -1) * (0.05 + 0.02 * world.rand.nextDouble()); - ParticleBuilder.create(Type.CLOUD) - .pos(caster.posX, caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2.5, caster.posZ) - .clr(0xffffff) - .spin(world.rand.nextDouble() * (radius - 1) + 0.5, speed) - .spawn(world); - } - } - - this.playSound(world, caster, ticksInUse, -1, modifiers); - return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/Intimidate.java b/src/main/java/electroblob/wizardry/spell/Intimidate.java index 39c722db..67fed76d 100644 --- a/src/main/java/electroblob/wizardry/spell/Intimidate.java +++ b/src/main/java/electroblob/wizardry/spell/Intimidate.java @@ -11,21 +11,20 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.ai.RandomPositionGenerator; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.pathfinding.PathPoint; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; +import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import java.util.List; +import javax.annotation.Nullable; @Mod.EventBusSubscriber -public class Intimidate extends Spell { +public class Intimidate extends SpellAreaEffect { /** The NBT tag name for storing the feared entity's UUID in the target's tag compound. */ public static final String NBT_KEY = "fearedEntity"; @@ -37,44 +36,46 @@ public class Intimidate extends Spell { public Intimidate(){ super("intimidate", SpellActions.SUMMON, false); - addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); + this.alwaysSucceed(true); + addProperties(EFFECT_DURATION, EFFECT_STRENGTH); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + public boolean canBeCastBy(TileEntityDispenser dispenser){ + return false; + } - if(!world.isRemote){ + @Override + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - List entities = EntityUtils.getEntitiesWithinRadius( - getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.range_upgrade), - caster.posX, caster.posY, caster.posZ, world, EntityCreature.class); + if(caster != null && target instanceof EntityCreature){ - for(EntityCreature target : entities){ - // Why do we need this here? - //runAway(target, caster); + int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)); - int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)); + NBTTagCompound entityNBT = target.getEntityData(); + if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); - NBTTagCompound entityNBT = target.getEntityData(); - if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); - - target.addPotionEffect(new PotionEffect(WizardryPotions.fear, - (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), - getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier)); - } - - }else{ - for(int i = 0; i < 30; i++){ - double x = caster.posX - 1 + world.rand.nextDouble() * 2; - double y = caster.getEntityBoundingBox().minY + 1.5 + world.rand.nextDouble() * 0.5; - double z = caster.posZ - 1 + world.rand.nextDouble() * 2; - ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.9f, 0.1f, 0).spawn(world); - } + target.addPotionEffect(new PotionEffect(WizardryPotions.fear, + (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), + getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier)); } - this.playSound(world, caster, ticksInUse, -1, modifiers); + return true; } + @Override + protected void spawnParticleEffect(World world, Vec3d origin, double radius, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + + if(caster != null) origin = caster.getPositionEyes(0); + + for(int i = 0; i < 30; i++){ + double x = origin.x - 1 + world.rand.nextDouble() * 2; + double y = origin.y - 0.25 + world.rand.nextDouble() * 0.5; + double z = origin.z - 1 + world.rand.nextDouble() * 2; + ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.9f, 0.1f, 0).spawn(world); + } + } + /** * Finds a random position away from the caster and sets the given target's AI path to that location. Defined here * so it can be used both in the spell itself and in the potion effect (event handler). diff --git a/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java index 4d530615..be00fa99 100644 --- a/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java +++ b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java @@ -3,63 +3,44 @@ package electroblob.wizardry.spell; import electroblob.wizardry.Wizardry; import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; -import net.minecraft.entity.player.EntityPlayer; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.init.MobEffects; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import java.util.List; +import javax.annotation.Nullable; -public class InvigoratingPresence extends Spell { +public class InvigoratingPresence extends SpellAreaEffect { public InvigoratingPresence(){ super("invigorating_presence", SpellActions.POINT_UP, false); this.soundValues(0.7f, 1.2f, 0.4f); - addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH); + this.alwaysSucceed(true); + this.targetAllies(true); + addProperties(EFFECT_DURATION, EFFECT_STRENGTH); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - List targets = EntityUtils.getEntitiesWithinRadius( - getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade), - caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); + int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)); - for(EntityPlayer target : targets){ - if(AllyDesignationSystem.isPlayerAlly(caster, target) || target == caster){ + target.addPotionEffect(new PotionEffect(MobEffects.STRENGTH, + (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), + getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier)); - int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)); - - target.addPotionEffect(new PotionEffect(MobEffects.STRENGTH, - (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), - getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier)); - } - } - - if(world.isRemote){ - - for(int i = 0; i < 50 * modifiers.get(WizardryItems.blast_upgrade); i++){ - - double radius = (1 + world.rand.nextDouble() * 4) * modifiers.get(WizardryItems.blast_upgrade); - float angle = world.rand.nextFloat() * (float)Math.PI * 2;; - - double x = caster.posX + radius * MathHelper.cos(angle); - double y = caster.getEntityBoundingBox().minY; - double z = caster.posZ + radius * MathHelper.sin(angle); - - ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50).clr(1, 0.2f, 0.2f).spawn(world); - - } - } - - playSound(world, caster, ticksInUse, -1, modifiers); return true; } + @Override + protected void spawnParticle(World world, double x, double y, double z){ + ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50).clr(1, 0.2f, 0.2f).spawn(world); + } + @Override protected String getTranslationKey(){ return Wizardry.tisTheSeason ? super.getTranslationKey() + "_festive" : super.getTranslationKey(); diff --git a/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java index 8d3bef3f..4bbac020 100644 --- a/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java +++ b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java @@ -2,82 +2,75 @@ package electroblob.wizardry.spell; import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; +import electroblob.wizardry.util.SpellModifiers; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.MobEffects; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import java.util.List; +import javax.annotation.Nullable; -public class PlagueOfDarkness extends Spell { +public class PlagueOfDarkness extends SpellAreaEffect { public PlagueOfDarkness(){ super("plague_of_darkness", SpellActions.POINT_DOWN, false); - addProperties(EFFECT_RADIUS, DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); + this.alwaysSucceed(true); + addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); soundValues(1, 1.1f, 0.2f); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - double radius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade); - - List targets = EntityUtils.getLivingWithinRadius(radius, caster.posX, caster.posY, caster.posZ, world); - - for(EntityLivingBase target : targets){ - if(AllyDesignationSystem.isValidTarget(caster, target) - && !MagicDamage.isEntityImmune(DamageType.WITHER, target)){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), - getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY)); - target.addPotionEffect(new PotionEffect(MobEffects.WITHER, - (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), - getProperty(EFFECT_STRENGTH).intValue() + SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)))); - } + if(!MagicDamage.isEntityImmune(DamageType.WITHER, target)){ + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), + getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY)); + target.addPotionEffect(new PotionEffect(MobEffects.WITHER, + (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), + getProperty(EFFECT_STRENGTH).intValue() + SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY)))); } - if(world.isRemote){ - - double particleX, particleZ; - - for(int i = 0; i < 40 * modifiers.get(WizardryItems.blast_upgrade); i++){ - - particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble(); - particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble(); - ParticleBuilder.create(Type.DARK_MAGIC).pos(particleX, caster.getEntityBoundingBox().minY, particleZ) - .vel(particleX - caster.posX, 0, particleZ - caster.posZ).clr(0.1f, 0, 0).spawn(world); - - particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble(); - particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble(); - ParticleBuilder.create(Type.SPARKLE).pos(particleX, caster.getEntityBoundingBox().minY, particleZ) - .vel(particleX - caster.posX, 0, particleZ - caster.posZ).time(30).clr(0.1f, 0, 0.05f).spawn(world); - - particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble(); - particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble(); - IBlockState block = BlockUtils.getBlockEntityIsStandingOn(caster); - - if(block != null){ - world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY, - particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, Block.getStateId(block)); - } - } - - ParticleBuilder.create(Type.SPHERE) - .pos(caster.posX, caster.getEntityBoundingBox().minY + 0.1, caster.posZ) - .scale((float)radius * 0.8f) - .clr(0.8f, 0, 0.05f) - .spawn(world); - } - - this.playSound(world, caster, ticksInUse, -1, modifiers); return true; } + @Override + protected void spawnParticleEffect(World world, Vec3d origin, double radius, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + + double particleX, particleZ; + + for(int i = 0; i < 40 * modifiers.get(WizardryItems.blast_upgrade); i++){ + + particleX = origin.x - 1.0d + 2 * world.rand.nextDouble(); + particleZ = origin.z - 1.0d + 2 * world.rand.nextDouble(); + ParticleBuilder.create(Type.DARK_MAGIC).pos(particleX, origin.y, particleZ) + .vel(particleX - origin.x, 0, particleZ - origin.z).clr(0.1f, 0, 0).spawn(world); + + particleX = origin.x - 1.0d + 2 * world.rand.nextDouble(); + particleZ = origin.z - 1.0d + 2 * world.rand.nextDouble(); + ParticleBuilder.create(Type.SPARKLE).pos(particleX, origin.y, particleZ) + .vel(particleX - origin.x, 0, particleZ - origin.z).time(30).clr(0.1f, 0, 0.05f).spawn(world); + + particleX = origin.x - 1.0d + 2 * world.rand.nextDouble(); + particleZ = origin.z - 1.0d + 2 * world.rand.nextDouble(); + + IBlockState block = world.getBlockState(new BlockPos(origin.x, origin.y - 0.5, origin.z)); + + if(block != null){ + world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, origin.y, + particleZ, particleX - origin.x, 0, particleZ - origin.z, Block.getStateId(block)); + } + } + + ParticleBuilder.create(Type.SPHERE).pos(origin.add(0, 0.1, 0)).scale((float)radius * 0.8f).clr(0.8f, 0, 0.05f).spawn(world); + } + } diff --git a/src/main/java/electroblob/wizardry/spell/Shockwave.java b/src/main/java/electroblob/wizardry/spell/Shockwave.java index 4ddc08f4..b6d1417d 100644 --- a/src/main/java/electroblob/wizardry/spell/Shockwave.java +++ b/src/main/java/electroblob/wizardry/spell/Shockwave.java @@ -4,23 +4,27 @@ import electroblob.wizardry.Wizardry; import electroblob.wizardry.item.ItemArtefact; import electroblob.wizardry.item.SpellActions; import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.*; +import electroblob.wizardry.util.EntityUtils; +import electroblob.wizardry.util.MagicDamage; import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.ParticleBuilder; import electroblob.wizardry.util.ParticleBuilder.Type; +import electroblob.wizardry.util.SpellModifiers; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.play.server.SPacketEntityVelocity; -import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.world.World; -import java.util.List; +import javax.annotation.Nullable; -public class Shockwave extends Spell { +public class Shockwave extends SpellAreaEffect { public static final String MAX_REPULSION_VELOCITY = "max_repulsion_velocity"; /** The radius within which maximum damage is dealt and maximum repulsion velocity is applied. */ @@ -29,104 +33,83 @@ public class Shockwave extends Spell { public Shockwave(){ super("shockwave", SpellActions.POINT_DOWN, false); this.soundValues(2, 0.5f, 0); - addProperties(BLAST_RADIUS, DAMAGE, MAX_REPULSION_VELOCITY); + addProperties(DAMAGE, MAX_REPULSION_VELOCITY); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){ - double radius = getProperty(BLAST_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade); + float radius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade); - List targets = EntityUtils.getLivingWithinRadius(radius, caster.posX, caster.posY, caster.posZ, world); + if(target instanceof EntityPlayer){ - for(EntityLivingBase target : targets){ + if(!Wizardry.settings.playersMoveEachOther) return false; - if(target instanceof EntityPlayer){ - - Wizardry.proxy.shakeScreen((EntityPlayer)target, 10); - - if(!Wizardry.settings.playersMoveEachOther) continue; - - if(ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring)){ - if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell.resist", - target.getName(), this.getNameForTranslationFormatted()), true); - continue; - } - } - - if(AllyDesignationSystem.isValidTarget(caster, target)){ - - // Produces a linear profile from 0 at the edge of the radius to 1 at the epicentre radius, then - // a constant value of 1 within the epicentre radius. - float proximity = (float)(1 - (Math.max(target.getDistance(caster) - EPICENTRE_RADIUS, 0))/(radius - EPICENTRE_RADIUS)); - - // Damage increases closer to player up to a maximum of 4 hearts (at 1 block distance). - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.BLAST), - getProperty(DAMAGE).floatValue() * proximity * modifiers.get(SpellModifiers.POTENCY)); - - if(!world.isRemote){ - - // Entity speed increases closer to the player to a maximum of 3 (at 1 block distance). - // This is the entity's speed compared to its distance from the player. Used for a similar triangles - // based x, y and z speed calculation. - double velocityFactor = proximity * getProperty(MAX_REPULSION_VELOCITY).floatValue(); - - double dx = target.posX - caster.posX; - double dy = target.getEntityBoundingBox().minY + 1 - caster.posY; - double dz = target.posZ - caster.posZ; - - target.motionX = velocityFactor * dx; - target.motionY = velocityFactor * dy; - target.motionZ = velocityFactor * dz; - - // Player motion is handled on that player's client so needs packets - if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); - } - } + if(ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring)){ + if(!world.isRemote && caster instanceof EntityPlayer) ((EntityPlayer)caster).sendStatusMessage( + new TextComponentTranslation("spell.resist", target.getName(), + this.getNameForTranslationFormatted()), true); + return false; } } - - if(world.isRemote){ - double particleX, particleZ; - - for(int i = 0; i < 40; i++){ + // Produces a linear profile from 0 at the edge of the radius to 1 at the epicentre radius, then + // a constant value of 1 within the epicentre radius. + float proximity = (float)(1 - (Math.max(origin.distanceTo(target.getPositionVector()) - EPICENTRE_RADIUS, 0))/(radius - EPICENTRE_RADIUS)); -// particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble(); -// particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble(); -// ParticleBuilder.create(Type.SPARKLE).pos(particleX, caster.getEntityBoundingBox().minY, particleZ) -// .vel(particleX - caster.posX, 0, particleZ - caster.posZ).time(30).clr(0.8f, 0.8f, 1).spawn(world); -// -// particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble(); -// particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble(); -// ParticleBuilder.create(Type.SPARKLE).pos(particleX, caster.getEntityBoundingBox().minY, particleZ) -// .vel(particleX - caster.posX, 0, particleZ - caster.posZ).time(30).clr(0.9f, 0.9f, 0.9f).spawn(world); + // Damage increases closer to player up to a maximum of 4 hearts (at 1 block distance). + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.BLAST), + getProperty(DAMAGE).floatValue() * proximity * modifiers.get(SpellModifiers.POTENCY)); - particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble(); - particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble(); - IBlockState block = BlockUtils.getBlockEntityIsStandingOn(caster); + if(!world.isRemote){ - if(block != null){ - world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY, - particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, Block.getStateId(block)); - } + // Entity speed increases closer to the player to a maximum of 3 (at 1 block distance). + // This is the entity's speed compared to its distance from the player. Used for a similar triangles + // based x, y and z speed calculation. + double velocityFactor = proximity * getProperty(MAX_REPULSION_VELOCITY).floatValue(); + + double dx = target.posX - origin.x; + double dy = target.getEntityBoundingBox().minY + 1 - origin.y; + double dz = target.posZ - origin.z; + + target.motionX = velocityFactor * dx; + target.motionY = velocityFactor * dy; + target.motionZ = velocityFactor * dz; + + // Player motion is handled on that player's client so needs packets + if(target instanceof EntityPlayerMP){ + ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); } - - ParticleBuilder.create(Type.SPHERE) - .pos(caster.posX, caster.getEntityBoundingBox().minY + 0.1, caster.posZ) - .scale((float)radius * 0.8f) - .clr(0.8f, 0.9f, 1) - .spawn(world); - - world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, caster.posX, - caster.getEntityBoundingBox().minY + 0.1, caster.posZ, 0, 0, 0); - } - - caster.swingArm(hand); - playSound(world, caster, ticksInUse, -1, modifiers); + return true; } + @Override + protected void spawnParticleEffect(World world, Vec3d origin, double radius, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + + // Can't put this in affectEntity(...) because it's only called for non-allies, plus here is client-side already + EntityUtils.getEntitiesWithinRadius(radius, origin.x, origin.y, origin.z, world, EntityPlayer.class) + .forEach(p -> Wizardry.proxy.shakeScreen(p, 10)); + + double particleX, particleZ; + + for(int i = 0; i < 40; i++){ + + particleX = origin.x - 1.0d + 2 * world.rand.nextDouble(); + particleZ = origin.z - 1.0d + 2 * world.rand.nextDouble(); + + IBlockState block = world.getBlockState(new BlockPos(origin.x, origin.y - 0.5, origin.z)); + + if(block != null){ + world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, origin.y, + particleZ, particleX - origin.x, 0, particleZ - origin.z, Block.getStateId(block)); + } + } + + ParticleBuilder.create(Type.SPHERE).pos(origin.add(0, 0.1, 0)).scale((float)radius * 0.8f).clr(0.8f, 0.9f, 1).spawn(world); + + world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, origin.x, origin.y + 0.1, origin.z, 0, 0, 0); + } + } diff --git a/src/main/java/electroblob/wizardry/spell/SpellAreaEffect.java b/src/main/java/electroblob/wizardry/spell/SpellAreaEffect.java index 502a81f3..c1c12d32 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellAreaEffect.java +++ b/src/main/java/electroblob/wizardry/spell/SpellAreaEffect.java @@ -5,33 +5,88 @@ import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.util.AllyDesignationSystem; import electroblob.wizardry.util.EntityUtils; import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; +import net.minecraft.tileentity.TileEntityDispenser; +import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import javax.annotation.Nullable; +import java.util.Comparator; import java.util.List; -/** [NYI] */ +/** + * Generic superclass for all spells which affect entities in a radius around the caster. This allows all + * the relevant code to be centralised. This class differs from most other spell superclasses in that it is abstract + * and as such must be subclassed to define what the spell actually does. This is because spells of this kind do a wider + * variety of different things, so it does not make sense to define more specific functions in this class since they + * would be redundant in the majority of cases. + *

+ * N.B. The abstract methods in this class have a {@link Nullable} caster parameter (the caster is null when the + * spell is cast by a dispenser). When implementing this method, be sure to check whether the caster is {@code null} + * and deal with it appropriately. + *

+ * Properties added by this type of spell: {@link Spell#EFFECT_RADIUS} + *

+ * By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)} + *

+ * By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)} + *

+ * By default, this type of spell requires a packet to be sent. {@link Spell#requiresPacket()} + * + * @author Electroblob + * @since Wizardry 4.3 + */ public abstract class SpellAreaEffect extends Spell { - - // TODO: This class doesn't really work as it is right now, it needs rethinking. The aim is to try and have all the - // different casting methods call a single (abstract) positional method to do the actual AoE. + /** True if this spell should target allies of the caster instead of hostiles. Positional casting always targets + * everything, regardless of this setting. */ + protected boolean targetAllies = false; + /** True if this spell should succeed even if no entities are affected, false if at least one entity must be affected. */ + protected boolean alwaysSucceed = false; /** The average number of particles to spawn per block in this spell's area of effect. */ protected float particleDensity = 0.65f; - public SpellAreaEffect(String name, EnumAction action){ - this(Wizardry.MODID, name, action); + public SpellAreaEffect(String name, EnumAction action, boolean continuous){ + this(Wizardry.MODID, name, action, continuous); } - public SpellAreaEffect(String modID, String name, EnumAction action){ - super(modID, name, action, false); + public SpellAreaEffect(String modID, String name, EnumAction action, boolean continuous){ + super(modID, name, action, continuous); this.addProperties(EFFECT_RADIUS); + this.npcSelector((e, o) -> true); } + /** + * Sets whether this spell should target allies of the caster instead of hostiles. Positional casting always targets + * everything, regardless of this setting. + * @param targetAllies True to call {@link SpellAreaEffect#affectEntity(World, Vec3d, EntityLivingBase, EntityLivingBase, int, int, SpellModifiers)} + * on allies of the caster, false to call it on entities considered hostile to the caster (not + * necessarily collectively-exhaustive; some entities may belong to neither category). + * @return The spell instance, allowing this method to be chained onto the constructor. + */ + public SpellAreaEffect targetAllies(boolean targetAllies) { + this.targetAllies = targetAllies; + return this; + } + + /** + * Sets whether this spell this spell should succeed even if no entities are affected. + * @param alwaysSucceed True if this spell should succeed even if no entities are affected, false if + * {@link SpellAreaEffect#affectEntity(World, Vec3d, EntityLivingBase, EntityLivingBase, int, int, SpellModifiers)} + * must return true at least once for the spell to succeed + * @return The spell instance, allowing this method to be chained onto the constructor. + */ + public SpellAreaEffect alwaysSucceed(boolean alwaysSucceed) { + this.alwaysSucceed = alwaysSucceed; + return this; + } + /** * Sets the number of particles to spawn per block for this spell. * @param particleDensity The average number of particles to spawn per block in this spell's area of effect. @@ -43,56 +98,92 @@ public abstract class SpellAreaEffect extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ - - List targets = EntityUtils.getEntitiesWithinRadius(getProperty(EFFECT_RADIUS).floatValue() - * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); - - targets.removeIf(target -> !AllyDesignationSystem.isValidTarget(caster, target)); - - for(EntityLivingBase target : targets){ - affectEntity(world, caster, target, modifiers); - } - - if(world.isRemote){ - spawnParticleEffect(world, caster, modifiers); - } - - this.playSound(world, caster, ticksInUse, -1, modifiers); + public boolean canBeCastBy(TileEntityDispenser dispenser){ return true; - } - + + @Override + public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){ + boolean result = findAndAffectEntities(world, new Vec3d(caster.posX, caster.getEntityBoundingBox().minY, caster.posZ), + caster, ticksInUse, modifiers); + if(result) this.playSound(world, caster, ticksInUse, -1, modifiers); + return result; + } + + @Override + public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + boolean result = findAndAffectEntities(world, caster.getPositionVector(), caster, ticksInUse, modifiers); + if(result) this.playSound(world, caster, ticksInUse, -1, modifiers); + return result; + } + + @Override + public boolean cast(World world, double x, double y, double z, EnumFacing direction, int ticksInUse, int duration, SpellModifiers modifiers){ + boolean result = findAndAffectEntities(world, new Vec3d(x, y, z), null, ticksInUse, modifiers); + if(result) this.playSound(world, x, y, z, ticksInUse, -1, modifiers); + return result; + } + + /** Takes care of the shared stuff for the three casting methods. This is mainly for internal use. */ + protected boolean findAndAffectEntities(World world, Vec3d origin, @Nullable EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){ + + double radius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade); + List targets = EntityUtils.getLivingWithinRadius(radius, origin.x, origin.y, origin.z, world); + + if(targetAllies){ + targets.removeIf(target -> target != caster && !AllyDesignationSystem.isAllied(caster, target)); + }else{ + targets.removeIf(target -> !AllyDesignationSystem.isValidTarget(caster, target)); + } + + // Sort by distance from the origin for consistency in ordering for spells with a limit + targets.sort(Comparator.comparingDouble(e -> e.getDistanceSq(origin.x, origin.y, origin.z))); + + boolean result = alwaysSucceed; + int i = 0; + + for(EntityLivingBase target : targets){ + if(affectEntity(world, origin, caster, target, i++, ticksInUse, modifiers)) result = true; + } + + if(world.isRemote) spawnParticleEffect(world, origin, radius, caster, modifiers); + return result; + } + /** * Called to do something to each entity within the spell's area of effect. * @param world The world in which the spell was cast. - * @param caster The entity that cast the spell. + * @param origin The position the spell was cast from. + * @param caster The entity that cast the spell, or null if it was cast from a position. * @param target The entity to do something to. + * @param targetCount The number of targets that have already been affected, useful for spells with a target limit. + * Targets will be called in order of distance from the caster/origin, + * @param ticksInUse The number of ticks the spell has already been cast for. * @param modifiers The modifiers the spell was cast with. + * @return True if whatever was done to the entity was successful, false if not. */ - protected abstract void affectEntity(World world, EntityLivingBase caster, EntityLivingBase target, SpellModifiers modifiers); + protected abstract boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers); /** * Called to spawn the spell's particle effect. By default, this generates a set of random points within the spell's * area of effect and calls {@link SpellAreaEffect#spawnParticle(World, double, double, double)} at each to spawn * the individual particles. Only called client-side. Override to add a custom particle effect. * @param world The world to spawn the particles in. - * @param caster The caster of the spell. + * @param origin The position the spell was cast from. + * @param radius The radius around the origin that was affected by this spell. + * @param caster The entity that cast the spell, or null if it was cast from a position. * @param modifiers The modifiers the spell was cast with. - */ - protected void spawnParticleEffect(World world, EntityLivingBase caster, SpellModifiers modifiers){ + */ + protected void spawnParticleEffect(World world, Vec3d origin, double radius, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ - double maxRadius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade); - int particleCount = (int)Math.round(particleDensity * Math.PI * maxRadius * maxRadius); + int particleCount = (int)Math.round(particleDensity * Math.PI * radius * radius); for(int i=0; i