diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 584af884..5246f4a7 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -789,6 +789,7 @@ public class ClientProxy extends CommonProxy { RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, RenderHammer::new); RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, RenderIceSpike::new); RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderForcefield::new); + RenderingRegistry.registerEntityRenderingHandler(EntityZombieSpawner.class, RenderZombieSpawner::new); //RenderingRegistry.registerEntityRenderingHandler(EntityContainmentField.class, RenderContainmentField::new); // Stuff that doesn't render diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderZombieSpawner.java b/src/main/java/electroblob/wizardry/client/renderer/RenderZombieSpawner.java new file mode 100644 index 00000000..e5c2f8ce --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderZombieSpawner.java @@ -0,0 +1,120 @@ +package electroblob.wizardry.client.renderer; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.construct.EntityZombieSpawner; +import electroblob.wizardry.util.WizardryUtilities; +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.AxisAlignedBB; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import org.lwjgl.opengl.GL11; + +public class RenderZombieSpawner extends Render { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/zombie_spawner.png"); + + private static final Vec3d[] HIDDEN_BOX = WizardryUtilities.getVertices(new AxisAlignedBB(-1, 0, -1, 1, 2.1, 1)); + + public RenderZombieSpawner(RenderManager renderManager){ + super(renderManager); + } + + @Override + public void doRender(EntityZombieSpawner entity, double x, double y, double z, float yaw, float partialTicks){ + + GlStateManager.pushMatrix(); + GlStateManager.enableBlend(); + GlStateManager.disableLighting(); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + GlStateManager.translate(x, y, z); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + + // The visible bit + GlStateManager.pushMatrix(); + + int animationTicks = 10; + float age = entity.ticksExisted + partialTicks; + float s = age < animationTicks ? age/animationTicks : MathHelper.clamp((entity.lifetime - age) / animationTicks, 0, 1); + s = (float)Math.pow(s, 0.4); // Smooths the animation + + GlStateManager.scale(s, s, s); + GlStateManager.rotate(age, 0, 1, 0); + + this.bindTexture(TEXTURE); + + Vec3d[] vertices = WizardryUtilities.getVertices(entity.getEntityBoundingBox().offset(entity.getPositionVector().scale(-1))); + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + + drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0, 0, 1, 1); // Top + drawFace(buffer, vertices[1], vertices[0], vertices[2], vertices[3], 0, 0, 1, 1); // Bottom + + tessellator.draw(); + + GlStateManager.popMatrix(); + + // Hidden box + GlStateManager.disableTexture2D(); + GlStateManager.colorMask(false, false, false, false); // Magically hide the zombies + + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR); + + // Outside (hides everything) + drawFaceColour(buffer, HIDDEN_BOX[6], HIDDEN_BOX[7], HIDDEN_BOX[2], HIDDEN_BOX[3], 0, 0, 0, 1); // South + drawFaceColour(buffer, HIDDEN_BOX[5], HIDDEN_BOX[6], HIDDEN_BOX[1], HIDDEN_BOX[2], 0, 0, 0, 1); // East + drawFaceColour(buffer, HIDDEN_BOX[4], HIDDEN_BOX[5], HIDDEN_BOX[0], HIDDEN_BOX[1], 0, 0, 0, 1); // North + drawFaceColour(buffer, HIDDEN_BOX[7], HIDDEN_BOX[4], HIDDEN_BOX[3], HIDDEN_BOX[0], 0, 0, 0, 1); // West + drawFaceColour(buffer, HIDDEN_BOX[5], HIDDEN_BOX[4], HIDDEN_BOX[6], HIDDEN_BOX[7], 0, 0, 0, 1); // Top + + tessellator.draw(); + + GlStateManager.disableBlend(); + GlStateManager.enableLighting(); + GlStateManager.disableRescaleNormal(); + GlStateManager.enableTexture2D(); + GlStateManager.colorMask(true, true, true, true); + GlStateManager.popMatrix(); + } + + @Override + public void renderMultipass(EntityZombieSpawner entity, double x, double y, double z, float yaw, float partialTicks){ + + + } + + @Override + public boolean isMultipass(){ + return false; + } + + @Override + protected ResourceLocation getEntityTexture(EntityZombieSpawner entity){ + return TEXTURE; + } + + 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(); + } + + private static void drawFaceColour(BufferBuilder buffer, Vec3d topLeft, Vec3d topRight, Vec3d bottomLeft, Vec3d bottomRight, float r, float g, float b, float a){ + buffer.pos(topLeft.x, topLeft.y, topLeft.z).color(r, g, b, a).endVertex(); + buffer.pos(topRight.x, topRight.y, topRight.z).color(r, g, b, a).endVertex(); + buffer.pos(bottomRight.x, bottomRight.y, bottomRight.z).color(r, g, b, a).endVertex(); + buffer.pos(bottomLeft.x, bottomLeft.y, bottomLeft.z).color(r, g, b, a).endVertex(); + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityZombieSpawner.java b/src/main/java/electroblob/wizardry/entity/construct/EntityZombieSpawner.java new file mode 100644 index 00000000..9c8f24d2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityZombieSpawner.java @@ -0,0 +1,63 @@ +package electroblob.wizardry.entity.construct; + +import electroblob.wizardry.entity.living.EntityZombieMinion; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.spell.SpellMinion; +import electroblob.wizardry.spell.ZombieApocalypse; +import electroblob.wizardry.util.ParticleBuilder; +import electroblob.wizardry.util.ParticleBuilder.Type; +import electroblob.wizardry.util.WizardryUtilities.Operations; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.ai.attributes.IAttributeInstance; +import net.minecraft.world.World; + +public class EntityZombieSpawner extends EntityMagicConstruct { + + public EntityZombieSpawner(World world){ + super(world); + this.setSize(4, 2); + } + + @Override + public void onUpdate(){ + + super.onUpdate(); + + if(!world.isRemote && rand.nextInt(Spells.zombie_apocalypse.getProperty(ZombieApocalypse.MINION_SPAWN_INTERVAL).intValue()) == 0){ + + EntityZombieMinion zombie = new EntityZombieMinion(world); + + zombie.setPosition(this.posX, this.posY, this.posZ); + zombie.setCaster(this.getCaster()); + // Modifier implementation + // Attribute modifiers are pretty opaque, see https://minecraft.gamepedia.com/Attribute#Modifiers + zombie.setLifetime(Spells.zombie_apocalypse.getProperty(SpellMinion.MINION_LIFETIME).intValue()); + IAttributeInstance attribute = zombie.getEntityAttribute(SharedMonsterAttributes.ATTACK_DAMAGE); + attribute.applyModifier(new AttributeModifier(SpellMinion.POTENCY_ATTRIBUTE_MODIFIER, + damageMultiplier - 1, Operations.MULTIPLY_CUMULATIVE)); + zombie.setHealth(zombie.getMaxHealth()); // Need to set this because we may have just modified the value + zombie.hurtResistantTime = 30; // Prevent fall damage + zombie.showSpawnParticles = false; // Hide spawn particles or they pop out the top of the hidden box + + world.spawnEntity(zombie); + } + + if(world.isRemote){ + + float b = 0.15f; + + for(double r = 1.5; r < 4; r += 0.2){ + ParticleBuilder.create(Type.CLOUD).clr(b-=0.02, 0, 0).pos(posX, posY - 0.3, posZ).scale(0.5f / (float)r) + .spin(r, 0.02/r * (1 + world.rand.nextDouble())).spawn(world); + } + + } + + } + + @Override + public boolean shouldRenderInPass(int pass){ + return super.shouldRenderInPass(pass); + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java index f61e33a3..861398cd 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java @@ -24,6 +24,8 @@ import java.util.UUID; public class EntityZombieMinion extends EntityZombie implements ISummonedCreature { + public boolean showSpawnParticles = true; + // Field implementations private int lifetime = -1; private UUID casterUUID; @@ -72,7 +74,7 @@ public class EntityZombieMinion extends EntityZombie implements ISummonedCreatur @Override public void onSpawn(){ - this.spawnParticleEffect(); + if(showSpawnParticles) this.spawnParticleEffect(); } @Override diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java index 704a956f..ff1c6b60 100644 --- a/src/main/java/electroblob/wizardry/registry/Spells.java +++ b/src/main/java/electroblob/wizardry/registry/Spells.java @@ -242,6 +242,8 @@ public final class Spells { public static final Spell stormcloud = placeholder(); + public static final Spell zombie_apocalypse = placeholder(); + @SubscribeEvent public static void register(RegistryEvent.Register event){ @@ -445,6 +447,8 @@ public final class Spells { registry.register(new Stormcloud()); + registry.register(new ZombieApocalypse()); + } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java index e89b5e79..d4162a34 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java @@ -146,6 +146,7 @@ public class WizardryEntities { registry.register(createEntry(EntityFireRing.class, "ring_of_fire", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityHealAura.class, "healing_aura", TrackingType.CONSTRUCT).build()); registry.register(createEntry(EntityDecay.class, "decay", TrackingType.CONSTRUCT).build()); + registry.register(createEntry(EntityZombieSpawner.class, "zombie_spawner", 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/spell/SpellMinion.java b/src/main/java/electroblob/wizardry/spell/SpellMinion.java index c9e8b1ed..bc047627 100644 --- a/src/main/java/electroblob/wizardry/spell/SpellMinion.java +++ b/src/main/java/electroblob/wizardry/spell/SpellMinion.java @@ -52,7 +52,7 @@ public class SpellMinion extends Spe * entity attribute modifier. */ public static final String HEALTH_MODIFIER = "minion_health"; /** The string identifier for the potency attribute modifier. */ - private static final String POTENCY_ATTRIBUTE_MODIFIER = "potency"; + public static final String POTENCY_ATTRIBUTE_MODIFIER = "potency"; /** A factory that creates summoned creature entities. */ protected final Function minionFactory; diff --git a/src/main/java/electroblob/wizardry/spell/ZombieApocalypse.java b/src/main/java/electroblob/wizardry/spell/ZombieApocalypse.java new file mode 100644 index 00000000..553041e7 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/ZombieApocalypse.java @@ -0,0 +1,27 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.entity.construct.EntityZombieSpawner; +import electroblob.wizardry.item.SpellActions; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class ZombieApocalypse extends SpellConstruct { + + public static final String MINION_SPAWN_INTERVAL = "minion_spawn_interval"; + + public ZombieApocalypse(){ + super("zombie_apocalypse", SpellActions.POINT_UP, EntityZombieSpawner::new, false); + addProperties(SpellMinion.MINION_LIFETIME, MINION_SPAWN_INTERVAL); + } + + @Override + protected boolean spawnConstruct(World world, double x, double y, double z, EnumFacing side, @Nullable EntityLivingBase caster, SpellModifiers modifiers){ + y += 8; + return super.spawnConstruct(world, x, y, z, side, caster, modifiers); + } + +} diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 539c1ce5..63af868e 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -494,6 +494,7 @@ entity.ebwizardry\:earthquake.name=Earthquake entity.ebwizardry\:hailstorm.name=Hailstorm entity.ebwizardry\:lightning_pulse.name=Lightning Pulse entity.ebwizardry\:stormcloud.name=Stormcloud +entity.ebwizardry\:zombie_spawner.name=Zombie Spawner itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -786,6 +787,7 @@ spell.ebwizardry\:water_breathing=Water Breathing spell.ebwizardry\:whirlwind=Whirlwind spell.ebwizardry\:wither=Wither spell.ebwizardry\:wither_skull=Wither Skull +spell.ebwizardry\:zombie_apocalypse=Zombie Apocalypse spell.ebwizardry\:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. spell.ebwizardry\:arc.desc=Fires a spark of lightning at the target. @@ -961,6 +963,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\: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... spell.ebwizardry\:invoke_weather.rain=The heavens open... diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 466f7a00..231d7891 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -494,6 +494,7 @@ entity.ebwizardry\:earthquake.name=Earthquake entity.ebwizardry\:hailstorm.name=Hailstorm entity.ebwizardry\:lightning_pulse.name=Lightning Pulse entity.ebwizardry\:stormcloud.name=Stormcloud +entity.ebwizardry\:zombie_spawner.name=Zombie Spawner itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -786,6 +787,7 @@ spell.ebwizardry\:water_breathing=Water Breathing spell.ebwizardry\:whirlwind=Whirlwind spell.ebwizardry\:wither=Wither spell.ebwizardry\:wither_skull=Wither Skull +spell.ebwizardry\:zombie_apocalypse=Zombie Apocalypse spell.ebwizardry\:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. spell.ebwizardry\:arc.desc=Fires a spark of lightning at the target. @@ -961,6 +963,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\: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... spell.ebwizardry\:invoke_weather.rain=The heavens open... diff --git a/src/main/resources/assets/ebwizardry/spells/zombie_apocalypse.json b/src/main/resources/assets/ebwizardry/spells/zombie_apocalypse.json new file mode 100644 index 00000000..13576fe7 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/spells/zombie_apocalypse.json @@ -0,0 +1,24 @@ +{ + "enabled": { + "book": true, + "scroll": true, + "wands": true, + "npcs": true, + "dispensers": true, + "commands": true, + "treasure": true, + "trades": true, + "looting": true + }, + "tier": "master", + "element": "necromancy", + "type": "construct", + "cost": 150, + "chargeup": 0, + "cooldown": 300, + "base_properties": { + "duration": 500, + "minion_lifetime": 600, + "minion_spawn_interval": 30 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/entity/zombie_spawner.png b/src/main/resources/assets/ebwizardry/textures/entity/zombie_spawner.png new file mode 100644 index 00000000..33a4f4b4 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/entity/zombie_spawner.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/spells/zombie_apocalypse.png b/src/main/resources/assets/ebwizardry/textures/spells/zombie_apocalypse.png new file mode 100644 index 00000000..5d4bc1d3 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/spells/zombie_apocalypse.png differ