From ba487bea41e95356144ed91adf9f9f2a418627d0 Mon Sep 17 00:00:00 2001 From: Electroblob77 <35599699+Electroblob77@users.noreply.github.com> Date: Fri, 21 Aug 2020 00:08:31 +0100 Subject: [PATCH] Add withering totem spell --- .../wizardry/client/ClientProxy.java | 1 + .../renderer/entity/RenderWitheringTotem.java | 159 ++++++++++++++++ .../construct/EntityWitheringTotem.java | 175 ++++++++++++++++++ .../electroblob/wizardry/registry/Spells.java | 2 + .../wizardry/registry/WizardryEntities.java | 1 + .../wizardry/registry/WizardrySounds.java | 2 + .../wizardry/spell/WitheringTotem.java | 25 +++ .../assets/ebwizardry/lang/en_gb.lang | 3 + .../assets/ebwizardry/lang/en_us.lang | 3 + .../resources/assets/ebwizardry/sounds.json | 3 + .../ebwizardry/spells/withering_totem.json | 29 +++ .../textures/spells/withering_totem.png | Bin 0 -> 5863 bytes 12 files changed, 403 insertions(+) create mode 100644 src/main/java/electroblob/wizardry/client/renderer/entity/RenderWitheringTotem.java create mode 100644 src/main/java/electroblob/wizardry/entity/construct/EntityWitheringTotem.java create mode 100644 src/main/java/electroblob/wizardry/spell/WitheringTotem.java create mode 100644 src/main/resources/assets/ebwizardry/spells/withering_totem.json create mode 100644 src/main/resources/assets/ebwizardry/textures/spells/withering_totem.png diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 5a9812c7..8683ed84 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -816,6 +816,7 @@ public class ClientProxy extends CommonProxy { RenderingRegistry.registerEntityRenderingHandler(EntityZombieSpawner.class, RenderZombieSpawner::new); RenderingRegistry.registerEntityRenderingHandler(EntityRadiantTotem.class, RenderRadiantTotem::new); RenderingRegistry.registerEntityRenderingHandler(EntityBoulder.class, RenderBoulder::new); + RenderingRegistry.registerEntityRenderingHandler(EntityWitheringTotem.class, RenderWitheringTotem::new); //RenderingRegistry.registerEntityRenderingHandler(EntityContainmentField.class, RenderContainmentField::new); // Stuff that doesn't render diff --git a/src/main/java/electroblob/wizardry/client/renderer/entity/RenderWitheringTotem.java b/src/main/java/electroblob/wizardry/client/renderer/entity/RenderWitheringTotem.java new file mode 100644 index 00000000..b5398be0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/entity/RenderWitheringTotem.java @@ -0,0 +1,159 @@ +package electroblob.wizardry.client.renderer.entity; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.DrawingUtils; +import electroblob.wizardry.entity.construct.EntityWitheringTotem; +import electroblob.wizardry.util.GeometryUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.Vec3d; +import org.lwjgl.opengl.GL11; + +import javax.annotation.Nullable; + +public class RenderWitheringTotem extends Render { + + private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/flare.png"); + private static final ResourceLocation[] CUBE_TEXTURES = new ResourceLocation[14]; + + static { + for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/cube_" + i + ".png"); + } + + public RenderWitheringTotem(RenderManager manager){ + super(manager); + } + + @Nullable + @Override + protected ResourceLocation getEntityTexture(EntityWitheringTotem entity){ + return CUBE_TEXTURES[entity.ticksExisted % CUBE_TEXTURES.length]; + } + + @Override + public void doRender(EntityWitheringTotem entity, double x, double y, double z, float entityYaw, float partialTicks){ + + Tessellator tessellator = Tessellator.getInstance(); + + GlStateManager.pushMatrix(); + + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + GlStateManager.depthMask(false); + GlStateManager.disableCull(); + + GlStateManager.translate(x, y + entity.height/2, z); + + float charge = entity.getHealthDrained() / 50f; + + float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10); + s *= 1 + charge * 0.3f; // Gets bigger the more health it drains + GlStateManager.scale(s, s, s); + + drawFlare(charge, tessellator); + drawCube(entity, tessellator, partialTicks); + + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GlStateManager.depthMask(true); + GlStateManager.enableCull(); + + GlStateManager.popMatrix(); + } + + private void drawFlare(float redness, Tessellator tessellator){ + + BufferBuilder buffer = tessellator.getBuffer(); + + GlStateManager.pushMatrix(); + + int c = DrawingUtils.mix(0xb333e6, 0xff0044, redness); + int r = c >> 16 & 255; + int g = c >> 8 & 255; + int b = c & 255; + + // Makes the colour add to the colour of the texture pixels, rather than the default multiplying + GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_ADD); + GlStateManager.color(r/255f, g/255f, b/255f); // Gets redder the more health it drains + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + bindTexture(FLARE_TEXTURE); + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 + ? Minecraft.getMinecraft().getRenderManager().playerViewX + : -Minecraft.getMinecraft().getRenderManager().playerViewX; + GlStateManager.rotate(180.0F - Minecraft.getMinecraft().getRenderManager().playerViewY, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + float radius = 0.5f; + + buffer.pos(-radius, radius, 0).tex(0, 0).endVertex(); + buffer.pos( radius, radius, 0).tex(1, 0).endVertex(); + buffer.pos( radius, -radius, 0).tex(1, 1).endVertex(); + buffer.pos(-radius, -radius, 0).tex(0, 1).endVertex(); + + tessellator.draw(); + + // Reverses the colour addition change + GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); + GlStateManager.color(1, 1, 1, 1); + GlStateManager.popMatrix(); + } + + private void drawCube(EntityWitheringTotem entity, Tessellator tessellator, float partialTicks){ + + BufferBuilder buffer = tessellator.getBuffer(); + + GlStateManager.pushMatrix(); + + float age = entity.ticksExisted + partialTicks; + float rotationSpeed = 2; + + GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F); + GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F); + GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F); + + GlStateManager.scale(0.5, 0.5, 0.5); + + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); + + bindEntityTexture(entity); + + Vec3d[] vertices = GeometryUtils.getVertices(entity.getEntityBoundingBox().offset(entity.getPositionVector() + .add(0, entity.height/2, 0).scale(-1))); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + // Outside + drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0.5f, 0, 0.75f, 0.5f); // Bottom + drawFace(buffer, vertices[6], vertices[7], vertices[2], vertices[3], 0.75f, 0.5f, 1, 1); // South + drawFace(buffer, vertices[5], vertices[6], vertices[1], vertices[2], 0, 0.5f, 0.25f, 1); // East + drawFace(buffer, vertices[4], vertices[5], vertices[0], vertices[1], 0.25f, 0.5f, 0.5f, 1); // North + drawFace(buffer, vertices[7], vertices[4], vertices[3], vertices[0], 0.5f, 0.5f, 0.75f, 1); // West + drawFace(buffer, vertices[5], vertices[4], vertices[6], vertices[7], 0.25f, 0, 0.5f, 0.5f); // Top + + tessellator.draw(); + + GlStateManager.popMatrix(); + } + + private static void drawFace(BufferBuilder buffer, Vec3d topLeft, Vec3d topRight, Vec3d bottomLeft, Vec3d bottomRight, float u1, float v1, float u2, float v2){ + buffer.pos(topLeft.x, topLeft.y, topLeft.z).tex(u1, v1).endVertex(); + buffer.pos(topRight.x, topRight.y, topRight.z).tex(u2, v1).endVertex(); + buffer.pos(bottomRight.x, bottomRight.y, bottomRight.z).tex(u2, v2).endVertex(); + buffer.pos(bottomLeft.x, bottomLeft.y, bottomLeft.z).tex(u1, v2).endVertex(); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityWitheringTotem.java b/src/main/java/electroblob/wizardry/entity/construct/EntityWitheringTotem.java new file mode 100644 index 00000000..fca25006 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityWitheringTotem.java @@ -0,0 +1,175 @@ +package electroblob.wizardry.entity.construct; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.spell.WitheringTotem; +import electroblob.wizardry.util.*; +import electroblob.wizardry.util.BlockUtils.SurfaceCriteria; +import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.util.ParticleBuilder.Type; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.MobEffects; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import java.util.Comparator; +import java.util.List; + +public class EntityWitheringTotem extends EntityScaledConstruct { + + private static final int PERIMETER_PARTICLE_DENSITY = 6; + + private static final DataParameter HEALTH_DRAINED = EntityDataManager.createKey(EntityWitheringTotem.class, DataSerializers.FLOAT); + + public EntityWitheringTotem(World world){ + super(world); + this.setSize(1, 1); // This entity is different in that its area of effect is kind of 'outside' it + } + + @Override + protected void entityInit(){ + dataManager.register(HEALTH_DRAINED, 0f); + } + + public float getHealthDrained(){ + return dataManager.get(HEALTH_DRAINED); + } + + public void addHealthDrained(float health){ + dataManager.set(HEALTH_DRAINED, getHealthDrained() + health); + } + + @Override + protected boolean shouldScaleWidth(){ + return false; + } + + @Override + protected boolean shouldScaleHeight(){ + return false; + } + + @Override + public void onUpdate(){ + + if(world.isRemote && this.ticksExisted == 1){ + Wizardry.proxy.playMovingSound(this, WizardrySounds.ENTITY_WITHERING_TOTEM_AMBIENT, WizardrySounds.SPELLS, 1, 1, true); + } + + super.onUpdate(); + + double radius = Spells.withering_totem.getProperty(Spell.EFFECT_RADIUS).floatValue() * sizeMultiplier; + + if(world.isRemote){ + + ParticleBuilder.create(Type.DUST, rand, posX, posY + 0.2, posZ, 0.3, false) + .vel(0, -0.02 - world.rand.nextFloat() * 0.01, 0).clr(0xf575f5).fade(0x382366).spawn(world); + + for(int i=0; i nearby = EntityUtils.getLivingWithinRadius(radius, posX, posY, posZ, world); + nearby.removeIf(e -> !isValidTarget(e)); + nearby.sort(Comparator.comparingDouble(e -> e.getDistanceSq(this))); + + int targetsRemaining = Spells.withering_totem.getProperty(WitheringTotem.MAX_TARGETS).intValue() + + (int)((damageMultiplier - 1) / Constants.POTENCY_INCREASE_PER_TIER); + + while(!nearby.isEmpty() && targetsRemaining > 0){ + + EntityLivingBase target = nearby.remove(0); + + if(EntityUtils.isLiving(target)){ + + if(target.ticksExisted % target.maxHurtResistantTime == 1){ + + float damage = Spells.withering_totem.getProperty(Spell.DAMAGE).floatValue(); + + if(EntityUtils.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, + getCaster(), DamageType.WITHER), damage)){ + addHealthDrained(damage); + } + } + + targetsRemaining--; + + if(world.isRemote){ + + Vec3d centre = GeometryUtils.getCentre(this); + Vec3d pos = GeometryUtils.getCentre(target); + + ParticleBuilder.create(Type.BEAM).pos(centre).target(target) + .clr(0.1f + 0.2f * world.rand.nextFloat(), 0, 0.3f).spawn(world); + + for(int i = 0; i < 3; i++){ + ParticleBuilder.create(Type.DUST, rand, pos.x, pos.y, pos.z, 0.3, false) + .vel(pos.subtract(centre).normalize().scale(-0.1)).clr(0x0c0024).fade(0x610017).spawn(world); + } + } + } + } + } + + @Override + public void despawn(){ + + double radius = Spells.withering_totem.getProperty(Spell.EFFECT_RADIUS).floatValue() * sizeMultiplier; + + List nearby = EntityUtils.getLivingWithinRadius(radius, posX, posY, posZ, world); + nearby.removeIf(e -> !isValidTarget(e)); + + float damage = Math.min(getHealthDrained() * 0.2f, Spells.withering_totem.getProperty(WitheringTotem.MAX_EXPLOSION_DAMAGE).floatValue()); + + for(EntityLivingBase target : nearby){ + + if(EntityUtils.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, + getCaster(), DamageType.MAGIC), damage)){ + target.addPotionEffect(new PotionEffect(MobEffects.WITHER, Spells.withering_totem.getProperty(Spell.EFFECT_DURATION).intValue(), + Spells.withering_totem.getProperty(Spell.EFFECT_STRENGTH).intValue())); + } + } + + if(world.isRemote) ParticleBuilder.create(Type.SPHERE).pos(GeometryUtils.getCentre(this)).scale((float)radius).clr(0xbe1a53) + .fade(0x210f4a).spawn(world); + + this.playSound(WizardrySounds.ENTITY_WITHERING_TOTEM_EXPLODE, 1, 1); + super.despawn(); + } + + // Usually damage multipliers don't need syncing, but here we're using it for the non-standard purpose of targeting + + @Override + public void writeSpawnData(ByteBuf data){ + super.writeSpawnData(data); + data.writeFloat(damageMultiplier); + } + + @Override + public void readSpawnData(ByteBuf data){ + super.readSpawnData(data); + damageMultiplier = data.readFloat(); + } +} diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java index e6327ce9..5dce3c2f 100644 --- a/src/main/java/electroblob/wizardry/registry/Spells.java +++ b/src/main/java/electroblob/wizardry/registry/Spells.java @@ -244,6 +244,7 @@ public final class Spells { public static final Spell mark_sacrifice = placeholder(); public static final Spell stormcloud = placeholder(); + public static final Spell withering_totem = placeholder(); public static final Spell fangs = placeholder(); public static final Spell radiant_totem = placeholder(); @@ -456,6 +457,7 @@ public final class Spells { registry.register(new MarkSacrifice()); registry.register(new Stormcloud()); + registry.register(new WitheringTotem()); registry.register(new Fangs()); registry.register(new RadiantTotem()); diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java index 6e144a6d..fe7de88e 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java @@ -148,6 +148,7 @@ public class WizardryEntities { registry.register(createEntry(EntityDecay.class, "decay", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityZombieSpawner.class, "zombie_spawner", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityRadiantTotem.class, "radiant_totem", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityWitheringTotem.class, "withering_totem", TrackingType.CONSTRUCT).build()); // These ones don't render, currently that makes no difference here but we might as well separate them registry.register(createEntry(EntityArrowRain.class, "arrow_rain", TrackingType.CONSTRUCT).build()); diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index 5794cc08..4842151a 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -73,6 +73,8 @@ public final class WizardrySounds { public static final SoundEvent ENTITY_RADIANT_TOTEM_VANISH = createSound("entity.radiant_totem.vanish"); public static final SoundEvent ENTITY_SHIELD_DEFLECT = createSound("entity.shield.deflect"); public static final SoundEvent ENTITY_TORNADO_AMBIENT = createSound("entity.tornado.ambient"); + public static final SoundEvent ENTITY_WITHERING_TOTEM_AMBIENT = createSound("entity.withering_totem.ambient"); + public static final SoundEvent ENTITY_WITHERING_TOTEM_EXPLODE = createSound("entity.withering_totem.explode"); public static final SoundEvent ENTITY_ZOMBIE_SPAWNER_SPAWN = createSound("entity.zombie_spawner.spawn"); public static final SoundEvent ENTITY_EVIL_WIZARD_AMBIENT = createSound("entity.evil_wizard.ambient"); diff --git a/src/main/java/electroblob/wizardry/spell/WitheringTotem.java b/src/main/java/electroblob/wizardry/spell/WitheringTotem.java new file mode 100644 index 00000000..93c69f64 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/WitheringTotem.java @@ -0,0 +1,25 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.entity.construct.EntityWitheringTotem; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.EnumFacing; + +import javax.annotation.Nullable; + +public class WitheringTotem extends SpellConstructRanged { + + public static final String MAX_TARGETS = "max_targets"; + public static final String MAX_EXPLOSION_DAMAGE = "max_explosion_damage"; + + public WitheringTotem(){ + super("withering_totem", EntityWitheringTotem::new, false); + this.addProperties(EFFECT_RADIUS, MAX_TARGETS, DAMAGE, MAX_EXPLOSION_DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH); + this.floor(false); + } + + @Override + protected void addConstructExtras(EntityWitheringTotem construct, EnumFacing side, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + construct.posY += 1.2; + } +} diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index d57eca84..f1bb201a 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -629,6 +629,7 @@ entity.ebwizardry\:stormcloud.name=Stormcloud entity.ebwizardry\:zombie_spawner.name=Zombie Spawner entity.ebwizardry\:boulder.name=Boulder entity.ebwizardry\:radiant_totem.name=Radiant Totem +entity.ebwizardry\:withering_totem.name=Withering Totem itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -938,6 +939,7 @@ spell.ebwizardry\:water_breathing=Water Breathing spell.ebwizardry\:whirlwind=Whirlwind spell.ebwizardry\:wither=Wither spell.ebwizardry\:wither_skull=Wither Skull +spell.ebwizardry\:withering_totem=Withering Totem spell.ebwizardry\:zombie_apocalypse=Zombie Apocalypse spell.ebwizardry\:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. @@ -1120,6 +1122,7 @@ spell.ebwizardry\:water_breathing.desc=Allows the caster to breathe underwater f spell.ebwizardry\:whirlwind.desc=Causes the target to be blown upwards and away from you at speed. spell.ebwizardry\:wither.desc=Fires a ray of darkness which withers anything it touches. spell.ebwizardry\:wither_skull.desc=Launches a wither skull in the direction you are pointing. +spell.ebwizardry\:withering_totem.desc=Summons a withering totem where you are pointing, which slowly drains health from nearby creatures. After 30 seconds, the totem explodes, releasing a wave of withering energy that deals more damage the more health that was drained. spell.ebwizardry\:zombie_apocalypse.desc=Opening a portal to the underworld is easy. The difficult part is shutting it again when things go wrong... spell.ebwizardry\:invoke_weather.sun=The rain begins to stop... diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 4559bc53..a5eac612 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -629,6 +629,7 @@ entity.ebwizardry\:stormcloud.name=Stormcloud entity.ebwizardry\:zombie_spawner.name=Zombie Spawner entity.ebwizardry\:boulder.name=Boulder entity.ebwizardry\:radiant_totem.name=Radiant Totem +entity.ebwizardry\:withering_totem.name=Withering Totem itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -938,6 +939,7 @@ spell.ebwizardry\:water_breathing=Water Breathing spell.ebwizardry\:whirlwind=Whirlwind spell.ebwizardry\:wither=Wither spell.ebwizardry\:wither_skull=Wither Skull +spell.ebwizardry\:withering_totem=Withering Totem spell.ebwizardry\:zombie_apocalypse=Zombie Apocalypse spell.ebwizardry\:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. @@ -1120,6 +1122,7 @@ spell.ebwizardry\:water_breathing.desc=Allows the caster to breathe underwater f spell.ebwizardry\:whirlwind.desc=Causes the target to be blown upwards and away from you at speed. spell.ebwizardry\:wither.desc=Fires a ray of darkness which withers anything it touches. spell.ebwizardry\:wither_skull.desc=Launches a wither skull in the direction you are pointing. +spell.ebwizardry\:withering_totem.desc=Summons a withering totem where you are pointing, which slowly drains health from nearby creatures. After 30 seconds, the totem explodes, releasing a wave of withering energy that deals more damage the more health that was drained. spell.ebwizardry\:zombie_apocalypse.desc=Opening a portal to the underworld is easy. The difficult part is shutting it again when things go wrong... spell.ebwizardry\:invoke_weather.sun=The rain begins to stop... diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index 47ec3571..19ab10ad 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -46,6 +46,8 @@ "entity.stormcloud.thunder": {"category": "spells", "sounds": ["ambient/weather/thunder1", "ambient/weather/thunder2", "ambient/weather/thunder3"]}, "entity.stormcloud.attack": {"category": "spells", "sounds": ["ebwizardry:arc1", "ebwizardry:arc2", "ebwizardry:arc3"]}, "entity.tornado.ambient": {"category": "spells", "sounds": ["ebwizardry:wind"]}, + "entity.withering_totem.ambient": {"category": "spells", "sounds": ["ebwizardry:dark_aura_loop"]}, + "entity.withering_totem.explode": {"category": "spells", "sounds": ["mob/wither/death"]}, "entity.zombie_spawner.spawn": {"category": "spells", "sounds": ["mob/guardian/elder_death"]}, "entity.evil_wizard.ambient": {"category": "hostile", "sounds": ["mob/evocation_illager/idle1", "mob/evocation_illager/idle2", "mob/evocation_illager/idle3", "mob/evocation_illager/idle4"]}, @@ -341,6 +343,7 @@ "spell.whirlwind": {"category": "spells", "sounds": ["ebwizardry:ice"]}, "spell.wither": {"category": "spells", "sounds": ["mob/wither/hurt1", "mob/wither/hurt2", "mob/wither/hurt3", "mob/wither/hurt4"]}, "spell.wither_skull": {"category": "spells", "sounds": ["mob/wither/shoot"]}, + "spell.withering_totem": {"category": "spells", "sounds": ["ebwizardry:beam_start"]}, "spell.zombie_apocalypse.start": {"category": "spells", "sounds": ["ebwizardry:dark_aura_start"]}, "spell.zombie_apocalypse.loop": {"category": "spells", "sounds": ["ebwizardry:dark_aura_loop"]}, "spell.zombie_apocalypse.end": {"category": "spells", "sounds": ["ebwizardry:dark_aura_end"]}, diff --git a/src/main/resources/assets/ebwizardry/spells/withering_totem.json b/src/main/resources/assets/ebwizardry/spells/withering_totem.json new file mode 100644 index 00000000..d5a7bae4 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/spells/withering_totem.json @@ -0,0 +1,29 @@ +{ + "enabled": { + "book": true, + "scroll": true, + "wands": true, + "npcs": true, + "dispensers": true, + "commands": true, + "treasure": true, + "trades": true, + "looting": true + }, + "tier": "advanced", + "element": "necromancy", + "type": "construct", + "cost": 50, + "chargeup": 20, + "cooldown": 200, + "base_properties": { + "range": 8, + "duration": 600, + "effect_radius": 6, + "max_targets": 1, + "damage": 1, + "max_explosion_damage": 10, + "effect_duration": 120, + "effect_strength": 0 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/spells/withering_totem.png b/src/main/resources/assets/ebwizardry/textures/spells/withering_totem.png new file mode 100644 index 0000000000000000000000000000000000000000..75ea6800b042e621605cf8f5a8273c2d22d2dd3d GIT binary patch literal 5863 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 hyfwG4x%qKFrEMEWs literal 0 HcmV?d00001