diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 589fb824..5a9812c7 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -815,6 +815,7 @@ public class ClientProxy extends CommonProxy { RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderForcefield::new); RenderingRegistry.registerEntityRenderingHandler(EntityZombieSpawner.class, RenderZombieSpawner::new); RenderingRegistry.registerEntityRenderingHandler(EntityRadiantTotem.class, RenderRadiantTotem::new); + RenderingRegistry.registerEntityRenderingHandler(EntityBoulder.class, RenderBoulder::new); //RenderingRegistry.registerEntityRenderingHandler(EntityContainmentField.class, RenderContainmentField::new); // Stuff that doesn't render diff --git a/src/main/java/electroblob/wizardry/client/model/ModelBoulder.java b/src/main/java/electroblob/wizardry/client/model/ModelBoulder.java new file mode 100644 index 00000000..7ee3fc64 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelBoulder.java @@ -0,0 +1,74 @@ +package electroblob.wizardry.client.model; + +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelBox; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; + +// Made with Blockbench 3.5.2 +public class ModelBoulder extends ModelBase { + + private final ModelRenderer mainGroup; + private final ModelRenderer box1; + private final ModelRenderer box2; + private final ModelRenderer box3; + private final ModelRenderer box4; + private final ModelRenderer box5; + private final ModelRenderer box6; + + public ModelBoulder(){ + + textureWidth = 64; + textureHeight = 32; + + mainGroup = new ModelRenderer(this); + mainGroup.setRotationPoint(0.0F, 24.0F, 0.0F); + + box1 = new ModelRenderer(this); + box1.setRotationPoint(0.0F, -19.0F, 0.0F); + mainGroup.addChild(box1); + box1.cubeList.add(new ModelBox(box1, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false)); + + box2 = new ModelRenderer(this); + box2.setRotationPoint(0.0F, -19.0F, 0.0F); + mainGroup.addChild(box2); + setRotationAngle(box2, -1.5708F, 0.0F, 0.0F); + box2.cubeList.add(new ModelBox(box2, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false)); + + box3 = new ModelRenderer(this); + box3.setRotationPoint(0.0F, -19.0F, 0.0F); + mainGroup.addChild(box3); + setRotationAngle(box3, 0.0F, -1.5708F, 0.0F); + box3.cubeList.add(new ModelBox(box3, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false)); + + box4 = new ModelRenderer(this); + box4.setRotationPoint(0.0F, -19.0F, 0.0F); + mainGroup.addChild(box4); + setRotationAngle(box4, 0.0F, 1.5708F, 0.0F); + box4.cubeList.add(new ModelBox(box4, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false)); + + box5 = new ModelRenderer(this); + box5.setRotationPoint(0.0F, -19.0F, 0.0F); + mainGroup.addChild(box5); + setRotationAngle(box5, 1.5708F, 0.0F, 0.0F); + box5.cubeList.add(new ModelBox(box5, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false)); + + box6 = new ModelRenderer(this); + box6.setRotationPoint(0.0F, -19.0F, 0.0F); + mainGroup.addChild(box6); + setRotationAngle(box6, 3.1416F, 0.0F, 0.0F); + box6.cubeList.add(new ModelBox(box6, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false)); + } + + @Override + public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){ + mainGroup.render(f5); + } + + public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z){ + modelRenderer.rotateAngleX = x; + modelRenderer.rotateAngleY = y; + modelRenderer.rotateAngleZ = z; + } + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/renderer/entity/RenderBoulder.java b/src/main/java/electroblob/wizardry/client/renderer/entity/RenderBoulder.java new file mode 100644 index 00000000..8fffdbd2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/entity/RenderBoulder.java @@ -0,0 +1,48 @@ +package electroblob.wizardry.client.renderer.entity; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.client.DrawingUtils; +import electroblob.wizardry.client.model.ModelBoulder; +import electroblob.wizardry.entity.construct.EntityBoulder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.util.ResourceLocation; + +public class RenderBoulder extends Render { + + private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/boulder.png"); + + private ModelBoulder model = new ModelBoulder(); + + public RenderBoulder(RenderManager manager){ + super(manager); + } + + @Override + public void doRender(EntityBoulder entity, double x, double y, double z, float yaw, float partialTicks){ + + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y + 1.2, z); + GlStateManager.rotate(180, 0F, 0F, 1F); + GlStateManager.rotate(yaw - 90, 0, 1, 0); + GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * partialTicks, 0, 0, 1); + GlStateManager.translate(0, entity.height/2 - 1.5, 0); + + // Pass in -1 for the lifetime as the boulder crumbles when the time expires + float s = DrawingUtils.smoothScaleFactor(-1, entity.ticksExisted, partialTicks, 10, 10); + GlStateManager.scale(s, s, s); + + this.bindTexture(TEXTURE); + + model.render(entity, 0, 0, 0, 0, 0, 0.0625f); + + GlStateManager.popMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(EntityBoulder entity){ + return TEXTURE; + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityBoulder.java b/src/main/java/electroblob/wizardry/entity/construct/EntityBoulder.java new file mode 100644 index 00000000..e380afeb --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityBoulder.java @@ -0,0 +1,221 @@ +package electroblob.wizardry.entity.construct; + +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Boulder; +import electroblob.wizardry.spell.Spell; +import electroblob.wizardry.util.BlockUtils; +import electroblob.wizardry.util.EntityUtils; +import electroblob.wizardry.util.MagicDamage; +import electroblob.wizardry.util.MagicDamage.DamageType; +import io.netty.buffer.ByteBuf; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.MoverType; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumParticleTypes; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +import java.util.List; + +public class EntityBoulder extends EntityScaledConstruct { + + private double velX, velZ; + private int hitsRemaining; + private boolean soundStarted = false; + + public EntityBoulder(World world){ + super(world); + setSize(2.375f, 2.375f); + this.noClip = false; + this.setNoGravity(false); + this.stepHeight = 1; + hitsRemaining = 5; + } + + public void setHorizontalVelocity(double velX, double velZ){ + this.velX = velX; + this.velZ = velZ; + } + + @Override + public void onUpdate(){ + + if(world.isRemote && !soundStarted && onGround){ + soundStarted = true; + Wizardry.proxy.playMovingSound(this, WizardrySounds.ENTITY_BOULDER_ROLL, WizardrySounds.SPELLS, 1, 1, true); + } + + super.onUpdate(); + + this.motionY -= 0.03999999910593033D; + this.move(MoverType.SELF, velX, motionY, velZ); + + List collided = world.getEntitiesWithinAABB(EntityLivingBase.class, this.getEntityBoundingBox()); + + float damage = Spells.boulder.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier; + float knockback = Spells.boulder.getProperty(Boulder.KNOCKBACK_STRENGTH).floatValue(); + + for(EntityLivingBase entity : collided){ + + if(!isValidTarget(entity)) break; + + if(EntityUtils.attackEntityWithoutKnockback(entity, MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), damage)){ + EntityUtils.applyStandardKnockback(this, entity, knockback); + entity.motionX += this.motionX; + entity.motionZ += this.motionZ; + } + entity.playSound(WizardrySounds.ENTITY_BOULDER_HIT, 1, 1); + } + + if(EntityUtils.canDamageBlocks(getCaster(), world) && collidedHorizontally){ + smashBlocks(BlockUtils.getBlockSphere(new BlockPos(posX + velX, posY + height / 2, posZ + velZ), height * 0.8), true); + } + } + + /** + * Attempts to smash the given list of blocks. If the boulder has exhausted its hits, or if any of the blocks are + * too hard to be smashed and breakIfTooHard is true, the boulder is destroyed. + * @param blocks A list of block positions at which any solid blocks that are weak enough will be destroyed + * @param breakIfTooHard True if the boulder should break if any of the blocks are too hard, false if it should stop + * on those blocks + * @return True if something was destroyed (either the boulder, one or more blocks, or both), false otherwise + */ + private boolean smashBlocks(List blocks, boolean breakIfTooHard){ + + if(blocks.removeIf(p -> world.getBlockState(p).getBlock().getExplosionResistance(world, p, this, null) > 3)){ + // If any of the blocks were not breakable, the boulder is smashed + if(breakIfTooHard){ + this.despawn(); + }else{ + return false; + } + + }else{ + + if(!world.isRemote){ + blocks.forEach(p -> world.destroyBlock(p, false)); + if(--hitsRemaining <= 0) this.despawn(); + }else{ + world.playSound(posX, posY, posZ, WizardrySounds.ENTITY_BOULDER_BREAK_BLOCK, SoundCategory.BLOCKS, 1, 1, false); + } + } + + shakeNearbyPlayers(); + return true; + } + + private void shakeNearbyPlayers(){ + EntityUtils.getEntitiesWithinRadius(10, posX, posY, posZ, world, EntityPlayer.class) + .forEach(p -> Wizardry.proxy.shakeScreen(p, 8)); + } + + @Override + public void despawn(){ + + if(world.isRemote){ + + for(int i = 0; i < 100; i++){ + double x = posX + (rand.nextDouble() - 0.5) * width; + double y = posY + rand.nextDouble() * height; + double z = posZ + (rand.nextDouble() - 0.5) * width; + world.spawnParticle(EnumParticleTypes.BLOCK_DUST, x, y, z, (x - posX) * 0.1, + (y - posY + height / 2) * 0.1, (z - posZ) * 0.1, Block.getStateId(Blocks.DIRT.getDefaultState())); + } + + world.playSound(posX, posY, posZ, WizardrySounds.ENTITY_BOULDER_BREAK_BLOCK, SoundCategory.BLOCKS, 1, 1, false); + } + + super.despawn(); + } + + @Override + public void move(MoverType type, double x, double y, double z){ + super.move(type, x, y, z); + this.rotationPitch += Math.toDegrees(MathHelper.sqrt(x*x + y*y + z*z) / (width/2)); // That's how we roll + } + + @Override + public void fall(float distance, float damageMultiplier){ + + super.fall(distance, damageMultiplier); + + if(EntityUtils.canDamageBlocks(getCaster(), world) && distance > 3){ + List sphere = BlockUtils.getBlockSphere(new BlockPos(posX + velX, posY + height / 2 + motionY, posZ + velZ), height * 0.8); + if(smashBlocks(sphere, distance > 8)) return; + hitsRemaining--; + } + + if(world.isRemote){ + + for(int i = 0; i < 40; i++){ + + double particleX = this.posX - 1.5 + 3 * rand.nextDouble(); + double particleZ = this.posZ - 1.5 + 3 * rand.nextDouble(); + // Roundabout way of getting a block instance for the block the boulder is standing on (if any). + IBlockState block = world.getBlockState(new BlockPos(this.posX, this.posY - 2, this.posZ)); + + if(block.getBlock() != Blocks.AIR){ + world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, this.posY, particleZ, + particleX - this.posX, 0, particleZ - this.posZ, Block.getStateId(block)); + } + } + + if(distance > 1.2){ + world.playSound(posX, posY, posZ, WizardrySounds.ENTITY_BOULDER_LAND, SoundCategory.BLOCKS, Math.min(2, distance / 4), 1, false); + shakeNearbyPlayers(); +// EntityUtils.getEntitiesWithinRadius(Math.min(12, distance * 2), posX, posY, posZ, world, EntityPlayer.class) +// .forEach(p -> Wizardry.proxy.shakeScreen(p, Math.min(12, distance * 2))); + } + } + } + + @Override + public boolean canBeCollidedWith(){ + return true; + } + + @Override + public AxisAlignedBB getCollisionBoundingBox(){ + return this.getEntityBoundingBox(); + } + + @Override + protected void readEntityFromNBT(NBTTagCompound nbt){ + super.readEntityFromNBT(nbt); + velX = nbt.getDouble("velX"); + velZ = nbt.getDouble("velZ"); + hitsRemaining = nbt.getInteger("hitsRemaining"); + } + + @Override + protected void writeEntityToNBT(NBTTagCompound nbt){ + super.writeEntityToNBT(nbt); + nbt.setDouble("velX", velX); + nbt.setDouble("velZ", velZ); + nbt.setInteger("hitsRemaining", hitsRemaining); + } + + @Override + public void writeSpawnData(ByteBuf data){ + super.writeSpawnData(data); + data.writeDouble(velX); + data.writeDouble(velZ); + } + + @Override + public void readSpawnData(ByteBuf data){ + super.readSpawnData(data); + this.velX = data.readDouble(); + this.velZ = data.readDouble(); + } + +} diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java index dee3c0db..fd4ee87a 100644 --- a/src/main/java/electroblob/wizardry/registry/Spells.java +++ b/src/main/java/electroblob/wizardry/registry/Spells.java @@ -248,6 +248,7 @@ public final class Spells { public static final Spell firestorm = placeholder(); public static final Spell zombie_apocalypse = placeholder(); + public static final Spell boulder = placeholder(); @SubscribeEvent public static void register(RegistryEvent.Register event){ @@ -458,6 +459,7 @@ public final class Spells { registry.register(new Firestorm()); registry.register(new ZombieApocalypse()); + registry.register(new Boulder()); } diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java index ae0c4010..6e144a6d 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardryEntities.java +++ b/src/main/java/electroblob/wizardry/registry/WizardryEntities.java @@ -160,6 +160,7 @@ public class WizardryEntities { registry.register(createEntry(EntityBubble.class, "bubble") .tracker(160, 3, false).build()); registry.register(createEntry(EntityTornado.class, "tornado") .tracker(160, 3, false).build()); registry.register(createEntry(EntityIceSpike.class, "ice_spike") .tracker(160, 1, true).build()); + registry.register(createEntry(EntityBoulder.class, "boulder") .tracker(160, 1, true).build()); // Vertical velocity is not constant } diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java index dcb80f07..5794cc08 100644 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java @@ -47,8 +47,12 @@ public final class WizardrySounds { public static final SoundEvent ENTITY_BLACK_HOLE_AMBIENT = createSound("entity.black_hole.ambient"); public static final SoundEvent ENTITY_BLACK_HOLE_VANISH = createSound("entity.black_hole.vanish"); public static final SoundEvent ENTITY_BLACK_HOLE_BREAK_BLOCK = createSound("entity.black_hole.break_block"); - public static final SoundEvent ENTITY_BUBBLE_POP = createSound("entity.bubble.pop"); public static final SoundEvent ENTITY_BLIZZARD_AMBIENT = createSound("entity.blizzard.ambient"); + public static final SoundEvent ENTITY_BOULDER_ROLL = createSound("entity.boulder.roll"); + public static final SoundEvent ENTITY_BOULDER_LAND = createSound("entity.boulder.land"); + public static final SoundEvent ENTITY_BOULDER_HIT = createSound("entity.boulder.hit"); + public static final SoundEvent ENTITY_BOULDER_BREAK_BLOCK = createSound("entity.boulder.break_block"); + public static final SoundEvent ENTITY_BUBBLE_POP = createSound("entity.bubble.pop"); public static final SoundEvent ENTITY_DECAY_AMBIENT = createSound("entity.decay.ambient"); public static final SoundEvent ENTITY_ENTRAPMENT_AMBIENT = createSound("entity.entrapment.ambient"); public static final SoundEvent ENTITY_ENTRAPMENT_VANISH = createSound("entity.entrapment.vanish"); diff --git a/src/main/java/electroblob/wizardry/spell/Boulder.java b/src/main/java/electroblob/wizardry/spell/Boulder.java new file mode 100644 index 00000000..b4debe62 --- /dev/null +++ b/src/main/java/electroblob/wizardry/spell/Boulder.java @@ -0,0 +1,34 @@ +package electroblob.wizardry.spell; + +import electroblob.wizardry.entity.construct.EntityBoulder; +import electroblob.wizardry.item.SpellActions; +import electroblob.wizardry.util.GeometryUtils; +import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumFacing.Axis; +import net.minecraft.util.math.Vec3d; + +public class Boulder extends SpellConstruct { + + public static final String SPEED = "speed"; + public static final String KNOCKBACK_STRENGTH = "knockback_strength"; + + public Boulder(){ + super("boulder", SpellActions.SUMMON, EntityBoulder::new, false); + addProperties(SPEED, DAMAGE, KNOCKBACK_STRENGTH); + } + + @Override + protected void addConstructExtras(EntityBoulder construct, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){ + float speed = getProperty(SPEED).floatValue(); + // Unlike tornado, boulder always has the same speed + Vec3d direction = caster == null ? new Vec3d(side.getDirectionVec()) + : GeometryUtils.replaceComponent(caster.getLookVec(), Axis.Y, 0).normalize(); + construct.setHorizontalVelocity(direction.x * speed, direction.z * speed); + construct.rotationYaw = caster == null ? side.getHorizontalAngle() : caster.rotationYaw; + double yOffset = caster == null ? 0 : 1.6; + construct.setPosition(construct.posX + direction.x, construct.posY + yOffset, construct.posZ + direction.z); + } + +} diff --git a/src/main/java/electroblob/wizardry/util/EntityUtils.java b/src/main/java/electroblob/wizardry/util/EntityUtils.java index e61fd228..62e10c4d 100644 --- a/src/main/java/electroblob/wizardry/util/EntityUtils.java +++ b/src/main/java/electroblob/wizardry/util/EntityUtils.java @@ -172,22 +172,34 @@ public final class EntityUtils { } /** - * Applies the standard (non-enchanted) amount of knockback to the given target, using the same calculation as - * {@link EntityLivingBase#attackEntityFrom(DamageSource, float)}. Use in conjunction with + * Applies the standard (non-enchanted) amount of knockback to the given target, using the same calculation and + * strength value (0.4) as {@link EntityLivingBase#attackEntityFrom(DamageSource, float)}. Use in conjunction with * {@link EntityUtils#attackEntityWithoutKnockback(Entity, DamageSource, float)} to change the source of * knockback for an attack. * - * @param attacker The entity that caused the knockback; the target will be pushed away from this entity. - * @param target The entity to be knocked back. + * @param attacker The entity that caused the knockback; the target will be pushed away from this entity + * @param target The entity to be knocked back */ public static void applyStandardKnockback(Entity attacker, EntityLivingBase target){ + applyStandardKnockback(attacker, target, 0.4f); + } + + /** + * Applies the standard knockback calculation to the given target, using the same calculation as + * {@link EntityLivingBase#attackEntityFrom(DamageSource, float)}. + * + * @param attacker The entity that caused the knockback; the target will be pushed away from this entity + * @param target The entity to be knocked back + * @param strength The strength of the knockback + */ + public static void applyStandardKnockback(Entity attacker, EntityLivingBase target, float strength){ double dx = attacker.posX - target.posX; double dz; for(dz = attacker.posZ - target.posZ; dx * dx + dz * dz < 1.0E-4D; dz = (Math.random() - Math.random()) * 0.01D){ dx = (Math.random() - Math.random()) * 0.01D; } - target.knockBack(attacker, 0.4f, dx, dz); + target.knockBack(attacker, strength, dx, dz); } /** @@ -344,7 +356,7 @@ public final class EntityUtils { /** Checks that the given entity is allowed to damage blocks in the given world. If the entity is a player or null, * this checks the player block damage config setting, otherwise it posts a mob griefing event and returns the result. */ - public static boolean canDamageBlocks(EntityLivingBase entity, World world){ + public static boolean canDamageBlocks(@Nullable EntityLivingBase entity, World world){ // TODO: Dispenser griefing! if(entity == null || entity instanceof EntityPlayer) return Wizardry.settings.playerBlockDamage; return ForgeEventFactory.getMobGriefingEvent(world, entity); diff --git a/src/main/resources/assets/ebwizardry/lang/en_gb.lang b/src/main/resources/assets/ebwizardry/lang/en_gb.lang index 170e7096..2edb12b6 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_gb.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_gb.lang @@ -625,6 +625,7 @@ entity.ebwizardry\:hailstorm.name=Hailstorm entity.ebwizardry\:lightning_pulse.name=Lightning Pulse entity.ebwizardry\:stormcloud.name=Stormcloud entity.ebwizardry\:zombie_spawner.name=Zombie Spawner +entity.ebwizardry\:boulder.name=Boulder itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -753,6 +754,7 @@ spell.ebwizardry\:black_hole=Black Hole spell.ebwizardry\:blinding_flash=Blinding Flash spell.ebwizardry\:blink=Blink spell.ebwizardry\:blizzard=Blizzard +spell.ebwizardry\:boulder=Boulder spell.ebwizardry\:bubble=Bubble spell.ebwizardry\:chain_lightning=Chain Lightning spell.ebwizardry\:charge=Charge @@ -933,6 +935,7 @@ spell.ebwizardry\:black_hole.desc=Tear reality asunder. spell.ebwizardry\:blinding_flash.desc=Emits an intense flash of light that blinds all nearby creatures. spell.ebwizardry\:blink.desc=Teleports the caster over a short distance to where they are pointing. spell.ebwizardry\:blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. +spell.ebwizardry\:boulder.desc="Let the world be my bowling alley, and my foes be the pins!" spell.ebwizardry\:bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. spell.ebwizardry\:chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. spell.ebwizardry\:charge.desc=Causes the caster to charge rapidly in the direction they are looking, damaging and knocking back anything in their path. diff --git a/src/main/resources/assets/ebwizardry/lang/en_us.lang b/src/main/resources/assets/ebwizardry/lang/en_us.lang index 79184bb9..22cd63a0 100644 --- a/src/main/resources/assets/ebwizardry/lang/en_us.lang +++ b/src/main/resources/assets/ebwizardry/lang/en_us.lang @@ -625,6 +625,7 @@ entity.ebwizardry\:hailstorm.name=Hailstorm entity.ebwizardry\:lightning_pulse.name=Lightning Pulse entity.ebwizardry\:stormcloud.name=Stormcloud entity.ebwizardry\:zombie_spawner.name=Zombie Spawner +entity.ebwizardry\:boulder.name=Boulder itemGroup.ebwizardry=Wizardry itemGroup.ebwizardryspells=Spells @@ -753,6 +754,7 @@ spell.ebwizardry\:black_hole=Black Hole spell.ebwizardry\:blinding_flash=Blinding Flash spell.ebwizardry\:blink=Blink spell.ebwizardry\:blizzard=Blizzard +spell.ebwizardry\:boulder=Boulder spell.ebwizardry\:bubble=Bubble spell.ebwizardry\:chain_lightning=Chain Lightning spell.ebwizardry\:charge=Charge @@ -933,6 +935,7 @@ spell.ebwizardry\:black_hole.desc=Tear reality asunder. spell.ebwizardry\:blinding_flash.desc=Emits an intense flash of light that blinds all nearby creatures. spell.ebwizardry\:blink.desc=Teleports the caster over a short distance to where they are pointing. spell.ebwizardry\:blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. +spell.ebwizardry\:boulder.desc="Let the world be my bowling alley, and my foes be the pins!" spell.ebwizardry\:bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. spell.ebwizardry\:chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. spell.ebwizardry\:charge.desc=Causes the caster to charge rapidly in the direction they are looking, damaging and knocking back anything in their path. diff --git a/src/main/resources/assets/ebwizardry/sounds.json b/src/main/resources/assets/ebwizardry/sounds.json index aa29195e..796542d4 100644 --- a/src/main/resources/assets/ebwizardry/sounds.json +++ b/src/main/resources/assets/ebwizardry/sounds.json @@ -18,8 +18,12 @@ "entity.black_hole.ambient": {"category": "spells", "sounds": ["portal/portal"]}, "entity.black_hole.vanish": {"category": "spells", "sounds": ["portal/trigger"]}, "entity.black_hole.break_block": {"category": "spells", "sounds": ["mob/zombie/woodbreak"]}, - "entity.bubble.pop": {"category": "spells", "sounds": ["random/pop"]}, "entity.blizzard.ambient": {"category": "spells", "sounds": ["ebwizardry:wind"]}, + "entity.boulder.roll": {"category": "spells", "sounds": ["ebwizardry:roll"]}, + "entity.boulder.land": {"category": "spells", "sounds": ["ebwizardry:rumble"]}, + "entity.boulder.hit": {"category": "spells", "sounds": ["damage/hit1", "damage/hit2", "damage/hit3"]}, + "entity.boulder.break_block": {"category": "spells", "sounds": ["mob/zombie/woodbreak"]}, + "entity.bubble.pop": {"category": "spells", "sounds": ["random/pop"]}, "entity.decay.ambient": {"category": "spells", "sounds": ["liquid/lava"]}, "entity.entrapment.ambient": {"category": "spells", "sounds": ["portal/portal"]}, "entity.entrapment.vanish": {"category": "spells", "sounds": ["portal/trigger"]}, @@ -130,6 +134,7 @@ "spell.blinding_flash": {"category": "spells", "sounds": ["ebwizardry:flash"]}, "spell.blink": {"category": "spells", "sounds": ["mob/endermen/portal", "mob/endermen/portal2"]}, "spell.blizzard": {"category": "spells", "sounds": ["ebwizardry:ice_age"]}, + "spell.boulder": {"category": "spells", "sounds": ["random/bow"]}, "spell.bubble.shoot": {"category": "spells", "sounds": ["ebwizardry:ice"]}, "spell.bubble.splash": {"category": "spells", "sounds": ["liquid/swim1", "liquid/swim2", "liquid/swim3", "liquid/swim4"], "volume": 2}, "spell.chain_lightning": {"category": "spells", "sounds": ["ebwizardry:arc1", "ebwizardry:arc2", "ebwizardry:arc3"]}, diff --git a/src/main/resources/assets/ebwizardry/sounds/roll.ogg b/src/main/resources/assets/ebwizardry/sounds/roll.ogg new file mode 100644 index 00000000..62a91264 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/sounds/roll.ogg differ diff --git a/src/main/resources/assets/ebwizardry/spells/boulder.json b/src/main/resources/assets/ebwizardry/spells/boulder.json new file mode 100644 index 00000000..6c1b1252 --- /dev/null +++ b/src/main/resources/assets/ebwizardry/spells/boulder.json @@ -0,0 +1,25 @@ +{ + "enabled": { + "book": true, + "scroll": true, + "wands": true, + "npcs": true, + "dispensers": true, + "commands": true, + "treasure": true, + "trades": true, + "looting": true + }, + "tier": "master", + "element": "earth", + "type": "attack", + "cost": 125, + "chargeup": 25, + "cooldown": 350, + "base_properties": { + "duration": 200, + "speed": 0.33, + "damage": 10, + "knockback_strength": 1 + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ebwizardry/textures/entity/boulder.png b/src/main/resources/assets/ebwizardry/textures/entity/boulder.png new file mode 100644 index 00000000..72d1e647 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/entity/boulder.png differ diff --git a/src/main/resources/assets/ebwizardry/textures/spells/boulder.png b/src/main/resources/assets/ebwizardry/textures/spells/boulder.png new file mode 100644 index 00000000..f0507e85 Binary files /dev/null and b/src/main/resources/assets/ebwizardry/textures/spells/boulder.png differ