Finish implementing SpellAreaEffect and retrofit various spell classes to extend it

This commit is contained in:
Electroblob77
2020-06-20 21:42:37 +01:00
parent 62b06f8fc6
commit 46a467de46
11 changed files with 434 additions and 400 deletions
@@ -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<EntityPlayer> 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();
@@ -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<EntityPlayer> 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!)
@@ -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
@@ -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<EntityLivingBase> 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
}
}
@@ -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<EntityLivingBase> 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;
}
}
@@ -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<EntityCreature> 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).
@@ -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<EntityPlayer> 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();
@@ -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<EntityLivingBase> 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);
}
}
@@ -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<EntityLivingBase> 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);
}
}
@@ -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.
* <p></p>
* <i>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.</i>
* <p></p>
* Properties added by this type of spell: {@link Spell#EFFECT_RADIUS}
* <p></p>
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastBy(EntityLiving, boolean)}
* <p></p>
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastBy(TileEntityDispenser)}
* <p></p>
* 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 <i>collectively-exhaustive</i>; 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<EntityLivingBase> 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<EntityLivingBase> 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<particleCount; i++){
double radius = (1 + world.rand.nextDouble() * (maxRadius - 1));
double r = (1 + world.rand.nextDouble() * (radius - 1));
float angle = world.rand.nextFloat() * (float)Math.PI * 2f;
spawnParticle(world, caster.posX + radius * MathHelper.cos(angle),
caster.getEntityBoundingBox().minY,
caster.posZ + radius * MathHelper.sin(angle));
spawnParticle(world, origin.x + r * MathHelper.cos(angle), origin.y, origin.z + r * MathHelper.sin(angle));
}
}