diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java b/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java index 4fb4ccf1..27c4d3fd 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/EntitySpell.java @@ -17,7 +17,7 @@ import com.minelittlepony.unicopia.spell.SpellRegistry; import net.minecraft.block.SoundType; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; @@ -35,7 +35,7 @@ import net.minecraft.util.SoundCategory; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -public class EntitySpell extends EntityLiving implements IMagicals, ICaster, IInAnimate { +public class EntitySpell extends EntityCreature implements IMagicals, ICaster, IInAnimate { private EntityLivingBase owner = null; diff --git a/src/main/java/com/minelittlepony/unicopia/particle/client/ParticleSphere.java b/src/main/java/com/minelittlepony/unicopia/particle/client/ParticleSphere.java index 8e52836a..6965cda5 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/client/ParticleSphere.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/client/ParticleSphere.java @@ -15,8 +15,6 @@ import com.minelittlepony.util.render.Color; public class ParticleSphere extends Particle implements IAttachableParticle { - private final float baseAlpha; - protected int tint; protected float alpha; @@ -28,6 +26,10 @@ public class ParticleSphere extends Particle implements IAttachableParticle { public ParticleSphere(int id, World w, double x, double y, double z, double vX, double vY, double vZ, int... args) { this(w, x, y, z, args[0], args[1], args[2]/255F); + + this.motionX = vX; + this.motionY = vY; + this.motionZ = vZ; } public ParticleSphere(World w, double x, double y, double z, int radius, int tint, float alpha) { @@ -36,9 +38,8 @@ public class ParticleSphere extends Particle implements IAttachableParticle { this.radius = radius; this.tint = tint; this.alpha = alpha; - this.baseAlpha = alpha; - this.setMaxAge(50000); + setMaxAge(10); } @Override @@ -48,29 +49,34 @@ public class ParticleSphere extends Particle implements IAttachableParticle { @Override public void attachTo(ICaster caster) { + setMaxAge(50000); this.caster = caster; } public void onUpdate() { super.onUpdate(); - alpha = Math.min(1F, 1 - (float)particleAge/particleMaxAge) * baseAlpha; - - if (caster == null || !caster.hasEffect() || caster.getEffect().getDead() || caster.getEntity().isDead) { - setExpired(); - } else { - Entity e = caster.getEntity(); - - if (!caster.getWorld().loadedEntityList.contains(caster.getEntity())) { + if (caster != null) { + if (!caster.hasEffect() || caster.getEffect().getDead() || caster.getEntity().isDead) { setExpired(); - } + } else { + Entity e = caster.getEntity(); - setPosition(e.posX, e.posY, e.posZ); + if (!caster.getWorld().loadedEntityList.contains(caster.getEntity())) { + setExpired(); + } + + setPosition(e.posX, e.posY, e.posZ); + } + } else { + if (this.rand.nextInt(10000) == 0) { + this.radius--; + } } } public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float x, float z, float yz, float xy, float xz) { - if (alpha <= 0) { + if (alpha <= 0 || radius <= 0) { return; } diff --git a/src/main/java/com/minelittlepony/unicopia/spell/CasterUtils.java b/src/main/java/com/minelittlepony/unicopia/spell/CasterUtils.java index 91e9ef54..5e6ddb33 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/CasterUtils.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/CasterUtils.java @@ -5,6 +5,7 @@ import java.util.stream.Stream; import javax.annotation.Nullable; +import com.google.common.collect.Streams; import com.minelittlepony.unicopia.Predicates; import com.minelittlepony.unicopia.player.PlayerSpeciesList; @@ -54,6 +55,12 @@ public class CasterUtils { .filter(e -> !e.getDead()); } + public static boolean isHoldingEffect(String effectName, Entity entity) { + return Streams.stream(entity.getEquipmentAndArmor()) + .map(SpellRegistry::getKeyFromStack) + .anyMatch(s -> s.equals(effectName)); + } + public static Optional> toCaster(@Nullable Entity entity) { if (entity instanceof ICaster) { diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellDarkness.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellDarkness.java new file mode 100644 index 00000000..a5e08326 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellDarkness.java @@ -0,0 +1,131 @@ +package com.minelittlepony.unicopia.spell; + +import java.util.Random; + +import com.minelittlepony.unicopia.UParticles; +import com.minelittlepony.unicopia.entity.EntitySpell; +import com.minelittlepony.unicopia.particle.Particles; +import com.minelittlepony.util.MagicalDamageSource; +import com.minelittlepony.util.shape.Sphere; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIAvoidEntity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.MobEffects; +import net.minecraft.init.SoundEvents; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +public class SpellDarkness extends AbstractSpell.RangedAreaSpell { + + final SoundEvent[] scarySounds = new SoundEvent[] { + SoundEvents.AMBIENT_CAVE, + SoundEvents.ENTITY_GHAST_SCREAM, + SoundEvents.ENTITY_ZOMBIE_AMBIENT, + SoundEvents.ENTITY_GHAST_AMBIENT, + SoundEvents.ENTITY_SKELETON_AMBIENT, + SoundEvents.ENTITY_SKELETON_SHOOT, + SoundEvents.ENTITY_ENDERDRAGON_GROWL, + SoundEvents.ENTITY_ENDERMEN_SCREAM, + SoundEvents.ENTITY_ZOMBIE_VILLAGER_AMBIENT + }; + + @Override + public String getName() { + return "darkness"; + } + + @Override + public int getTint() { + return 0x000000; + } + + @Override + public boolean allowAI() { + return true; + } + + @Override + public void onPlaced(ICaster caster) { + if (caster.getEntity() instanceof EntitySpell) { + EntitySpell living = (EntitySpell)caster.getEntity(); + + living.tasks.addTask(1, new EntityAIAvoidEntity<>(living, EntityPlayer.class, 3, 4, 4)); + living.height = 1.8F; + + living.setPosition(living.posX, living.posY, living.posZ); + } + } + + @Override + public boolean update(ICaster source) { + int radius = 7 + (source.getCurrentLevel() * 3); + + if (source.getWorld().rand.nextInt(15) == 0) { + source.getWorld().playSound(null, source.getOrigin(), getScarySoundEffect(source.getWorld().rand), SoundCategory.AMBIENT, + 0.2F + source.getWorld().rand.nextFloat(), 0.3F); + } + source.findAllEntitiesInRange(radius * 1.5F) + .filter(e -> e instanceof EntityLivingBase) + .map(EntityLivingBase.class::cast) + .forEach(e -> applyDarkness(source, e)); + + return false; + } + + protected void applyDarkness(ICaster source, EntityLivingBase entity) { + if (!CasterUtils.isHoldingEffect("light", entity)) { + entity.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 100, 3)); + + Vec3d origin = source.getOriginVector(); + Vec3d to = entity.getPositionVector(); + double distance = origin.distanceTo(entity.getPositionVector()); + + if (entity.world.rand.nextInt(30) == 0) { + double appliedForce = distance / 10 + entity.world.rand.nextInt(3); + Vec3d force = origin.subtract(to).normalize().scale(appliedForce); + + entity.addVelocity(force.x, force.y, force.z); + } + + if (distance < 3 && entity.world.rand.nextInt(30) == 0) { + entity.attackEntityFrom(MagicalDamageSource.create("darkness"), 3); + } + + } else { + if (entity.isPotionActive(MobEffects.BLINDNESS)) { + entity.removeActivePotionEffect(MobEffects.BLINDNESS); + } + } + } + + protected SoundEvent getScarySoundEffect(Random rand) { + return scarySounds[rand.nextInt(scarySounds.length)]; + } + + @Override + public void render(ICaster source) { + int radius = 7 + (source.getCurrentLevel() * 3); + + source.spawnParticles(new Sphere(false, radius), radius * 6, pos -> { + if (!source.getWorld().isAirBlock(new BlockPos(pos).down())) { + int size = source.getWorld().rand.nextInt(4); + + if (size > 0) { + double vX = (source.getWorld().rand.nextFloat() - 0.5) * 2; + double vZ = (source.getWorld().rand.nextFloat() - 0.5) * 2; + + Particles.instance().spawnParticle(UParticles.SPHERE, false, pos, vX, 0, vZ, size, getTint(), 100); + } + } + }); + } + + @Override + public SpellAffinity getAffinity() { + return SpellAffinity.BAD; + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java index 7a4049a2..3f3b8cf1 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellDrake.java @@ -17,8 +17,6 @@ public class SpellDrake extends AbstractSpell { @Nullable private IMagicEffect piggyBackSpell; - private boolean firstUpdate = true; - @Override public String getName() { return "drake"; @@ -26,7 +24,7 @@ public class SpellDrake extends AbstractSpell { @Override public SpellAffinity getAffinity() { - return SpellAffinity.NEUTRAL; + return SpellAffinity.GOOD; } @Override @@ -53,23 +51,21 @@ public class SpellDrake extends AbstractSpell { } @Override - public boolean update(ICaster source) { + public void onPlaced(ICaster caster) { + if (caster.getEntity() instanceof EntitySpell) { + EntitySpell living = (EntitySpell)caster.getEntity(); - if (firstUpdate) { - firstUpdate = false; + ((PathNavigateGround)living.getNavigator()).setCanSwim(false); + living.tasks.addTask(1, new EntityAISwimming(living)); + living.tasks.addTask(2, new EntityAIFollowCaster<>(caster, 1, 4, 70)); + living.height = 1.8F; - if (source.getEntity() instanceof EntitySpell) { - EntitySpell living = (EntitySpell)source.getEntity(); - - ((PathNavigateGround)living.getNavigator()).setCanSwim(false); - living.tasks.addTask(1, new EntityAISwimming(living)); - living.tasks.addTask(2, new EntityAIFollowCaster<>(source, 1, 4, 70)); - living.height = 1.8F; - - living.setPosition(living.posX, living.posY, living.posZ); - } + living.setPosition(living.posX, living.posY, living.posZ); } + } + @Override + public boolean update(ICaster source) { if (piggyBackSpell == null) { AxisAlignedBB bb = EFFECT_BOUNDS.offset(source.getOriginVector()); diff --git a/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java b/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java index b79832f9..ae51fb51 100644 --- a/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java +++ b/src/main/java/com/minelittlepony/unicopia/spell/SpellRegistry.java @@ -43,6 +43,7 @@ public class SpellRegistry { registerSpell(SpellInferno::new); registerSpell(SpellDrake::new); registerSpell(SpellReveal::new); + registerSpell(SpellDarkness::new); registerSpell(GenericSpell.factory("light", 0xF7FACB, SpellAffinity.GOOD)); } diff --git a/src/main/resources/assets/unicopia/lang/en_US.lang b/src/main/resources/assets/unicopia/lang/en_US.lang index d695bff8..8abde4d5 100644 --- a/src/main/resources/assets/unicopia/lang/en_US.lang +++ b/src/main/resources/assets/unicopia/lang/en_US.lang @@ -63,11 +63,17 @@ spell.ice.name=Frost spell.ice.tagline=Ice I spell.drake.name=Assistance -spell.drake.tagline=Helpfulness I +spell.drake.tagline=Golomancy I curse.shield.name=Repulsion curse.shield.tagline=Hostility I +curse.darkness.name=Darkness +curse.darkness.tagline=Golomancy I/Resurrection II + +curse.harm.name=Hurting +curse.harm.tagline=Hostility II + curse.awkward.name=Awkwardness curse.awkward.tagline=*Derp* @@ -77,9 +83,6 @@ curse.vortex.tagline=Torture I curse.necromancy.name=Necromancy curse.necromancy.tagline=Resurrection I -curse.drake.name=Assistance -curse.drake.tagline=Unhelpfulness I - curse.inferno.name=Inferno curse.inferno.tagline=Fire II @@ -218,6 +221,7 @@ unicopia.power.disguise=Secondary Changeling ability unicopia.gui.title.bagofholding=Bag of Holding +death.attack.darkness=%1$s went missing death.attack.feed=%1$s was drained of all life death.attack.feed.player=%1$s died to feed %2$s death.attack.cold=%1$s died of frost bite