diff --git a/src/main/java/app/simplexdev/arcanumocculta/api/spell/AbstractSpell.java b/src/main/java/app/simplexdev/arcanumocculta/api/spell/AbstractSpell.java index 61a67ba..e05d3e3 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/api/spell/AbstractSpell.java +++ b/src/main/java/app/simplexdev/arcanumocculta/api/spell/AbstractSpell.java @@ -5,7 +5,6 @@ import app.simplexdev.arcanumocculta.api.caster.CasterLevel; import app.simplexdev.arcanumocculta.api.spell.enums.Damages; import app.simplexdev.arcanumocculta.api.spell.enums.Durations; import app.simplexdev.arcanumocculta.api.spell.enums.ManaCosts; -import app.simplexdev.arcanumocculta.util.SpellUtils; import java.util.List; import java.util.SplittableRandom; import org.bukkit.Bukkit; @@ -22,7 +21,6 @@ import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionData; -import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionType; import org.bukkit.util.Vector; @@ -53,6 +51,20 @@ public abstract class AbstractSpell implements Spell this.coolDown = coolDown; } + private static ItemDisplay createProjectile(final Material visual, final World world, final Location location, + final Vector velocity) + { + final ItemDisplay display = (ItemDisplay) world.spawnEntity(location, EntityType.ITEM_DISPLAY); + display.setItemStack(new ItemStack(visual)); + display.setGravity(true); + display.setPersistent(false); + display.setSilent(true); + display.setShadowRadius(0); + display.setShadowStrength(0); + display.setVelocity(velocity); + return display; + } + @Override public String getName() { @@ -155,10 +167,31 @@ public abstract class AbstractSpell implements Spell getSpellEffects())); } - public void simulateExplosion(final Location location, final float size, boolean breakBlocks) { + public void simulateExplosion(final Location location, final float size, boolean breakBlocks) + { location.getWorld().createExplosion(location, size, true, breakBlocks); } + public Vector tracerVector(final Caster caster) + { + return caster.bukkit().getLocation().clone().getDirection().multiply(2); + } + + public Location topLocation(final Caster caster) + { + final World world = caster.bukkit().getWorld(); + final Location eyeLocation = caster.bukkit().getEyeLocation().clone(); + final int diff = world.getMaxHeight() - eyeLocation.getBlockY(); + return eyeLocation.add(0, diff, 0); + } + + public Vector meteorVector(final Caster caster) + { + final Location topLocation = topLocation(caster); + final Vector v = topLocation.getDirection().normalize(); + return v.multiply(new Vector(0, -5, 0)); + } + public Entity prepareProjectile(final Caster caster, final Material visual, final Vector velocity) { final double expMod = getLevelRequirement().getExperienceMarker(); @@ -166,13 +199,13 @@ public abstract class AbstractSpell implements Spell final Player player = caster.bukkit(); final Location location = player.getLocation().clone().add(0, player.getEyeHeight(), 0); final Entity projectile = createProjectile(visual, player.getWorld(), location, - velocity); + velocity); caster.removeMana(manaCost().getManaCost()); caster.addExperience(random().nextDouble(expMod * 0.25)); return projectile; } - public void tracer(final World world, final Location location, final Particle particle) + public void tracerDirectional(final World world, final Location location, final Particle particle) { world.spawnParticle(particle, location, @@ -182,6 +215,13 @@ public abstract class AbstractSpell implements Spell random().nextDouble(-2, 2)); } + public void tracerRGB(final World world, final Location location, + final Particle particle, final int r, + final int g, final int b) + { + world.spawnParticle(particle, location, 0, r, g, b); + } + public void spiral(final World world, final Location location, final Particle particle) { final double step = 0.5; @@ -198,16 +238,18 @@ public abstract class AbstractSpell implements Spell } public AreaEffectCloud cloud(final World world, - final Location location, - final Particle particle, - final float size, - final int duration, - final PotionType effect) { + final Location location, + final Particle particle, + final float size, + final int duration, + final PotionType effect) + { AreaEffectCloud cloud = (AreaEffectCloud) world.spawnEntity(location, EntityType.AREA_EFFECT_CLOUD); cloud.setParticle(particle); cloud.setDuration(duration); - if (effect != null) { + if (effect != null) + { cloud.setBasePotionData(new PotionData(effect)); } @@ -218,23 +260,9 @@ public abstract class AbstractSpell implements Spell } protected void applyEffectsIndividually(final LivingEntity target, - final Caster caster, final SpellEffect... effects) + final Caster caster, final SpellEffect... effects) { for (final SpellEffect effect : effects) effect.apply(target, caster); } - - private static ItemDisplay createProjectile(final Material visual, final World world, final Location location, - final Vector velocity) - { - final ItemDisplay display = (ItemDisplay) world.spawnEntity(location, EntityType.ITEM_DISPLAY); - display.setItemStack(new ItemStack(visual)); - display.setGravity(true); - display.setPersistent(false); - display.setSilent(true); - display.setShadowRadius(0); - display.setShadowStrength(0); - display.setVelocity(velocity); - return display; - } } diff --git a/src/main/java/app/simplexdev/arcanumocculta/api/spell/enums/SpellTypes.java b/src/main/java/app/simplexdev/arcanumocculta/api/spell/enums/SpellTypes.java index 617d9ff..9100ace 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/api/spell/enums/SpellTypes.java +++ b/src/main/java/app/simplexdev/arcanumocculta/api/spell/enums/SpellTypes.java @@ -23,16 +23,17 @@ package app.simplexdev.arcanumocculta.api.spell.enums; // Reference for implementation. +// Remove entries when complete in order to keep track of progress. public enum SpellTypes { - SOUL_PEBBLE, SOUL_SHARD, LARGE_SOUL_SHARD, SOUL_COMET, SOUL_SPIRAL, + LARGE_SOUL_SHARD, SOUL_COMET, SOUL_SPIRAL, SOUL_SHOWER, SOUL_ARC, SOUL_BARRAGE, SOUL_BURST, SOUL_CANNON, SOUL_HAMMER, SOUL_BLAST, SOUL_EXPLOSION, SOUL_PHALANX, GREAT_SOUL_PHALANX, SOUL_MIST, ARS_NOVA, ARS_ENIGMA, ARS_ULTIMA, - FLAME_SLING, FLAME_BURST, CATCH_FLAME, FLAME_BARRAGE, FIREBALL, METEORITE, METEOR, FLAME_WHIP, FLAME_FAN, + FLAME_BURST, CATCH_FLAME, FLAME_BARRAGE, FIREBALL, METEORITE, METEOR, FLAME_WHIP, FLAME_FAN, FLAME_ORB, FLAME_PILLARS, FLAME_WHIRLWIND, - WITHER_BURST, WITHER_SHOT, WITHER_SHARD, WITHER_COMET, WITHER_METEORITE, + WITHER_BURST, WITHER_SHOT, WITHER_SHARD, WITHER_COMET, HEALING, GREATER_HEALING, HEAL_OTHER, HEALING_HANDS, HEALING_WAVE, GUARDIAN_PROTECTION, SAVIORS_BLESSING, DIVING_BLESSING, DIVINE_GRACE, ULTIMATE_HEALING, DIVINE_INTERVENTION, DIVINE_RETRIBUTION, diff --git a/src/main/java/app/simplexdev/arcanumocculta/spells/flame/FlameSling.java b/src/main/java/app/simplexdev/arcanumocculta/spells/flame/FlameSling.java index 7563e9c..8f3f4d5 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/spells/flame/FlameSling.java +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/flame/FlameSling.java @@ -63,9 +63,7 @@ public class FlameSling extends AbstractSpell return; final Entity projectile = prepareProjectile(caster, Material.FIRE_CHARGE, - caster.bukkit() - .getLocation().clone() - .getDirection().multiply(2)); + tracerVector(caster)); while (!projectile.isDead()) { if (!projectile.getNearbyEntities(1, 1, 1).isEmpty()) { diff --git a/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulPebble.java b/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulPebble.java index 9ca35f0..0aa9c63 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulPebble.java +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulPebble.java @@ -44,15 +44,11 @@ public final class SoulPebble extends AbstractSpell } final Entity projectile = prepareProjectile(caster, Material.AIR, - caster.bukkit() - .getLocation() - .clone() - .getDirection() - .multiply(2)); + tracerVector(caster)); while (!projectile.isOnGround() || !projectile.isDead()) { - tracer(projectile.getWorld(), projectile.getLocation(), Particle.SOUL); + tracerDirectional(projectile.getWorld(), projectile.getLocation(), Particle.SOUL); if (!projectile.getNearbyEntities(1, 1, 1).isEmpty()) { diff --git a/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulShard.java b/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulShard.java index 92a7f69..c573779 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulShard.java +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulShard.java @@ -64,17 +64,13 @@ public final class SoulShard extends AbstractSpell } final Entity projectile = prepareProjectile(caster, Material.AIR, - caster.bukkit() - .getLocation() - .clone() - .getDirection() - .multiply(2)); + tracerVector(caster)); while (!projectile.isDead()) { for (int i = 0; i < 3; i++) { - tracer(projectile.getWorld(), projectile.getLocation(), Particle.SOUL); + tracerDirectional(projectile.getWorld(), projectile.getLocation(), Particle.SOUL); } if (!projectile.getNearbyEntities(1, 1, 1).isEmpty()) diff --git a/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherBurst.java b/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherBurst.java new file mode 100644 index 0000000..cbc794e --- /dev/null +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherBurst.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Simplex Development Group + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * with the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package app.simplexdev.arcanumocculta.spells.wither; + +import app.simplexdev.arcanumocculta.api.caster.Caster; +import app.simplexdev.arcanumocculta.api.caster.CasterLevel; +import app.simplexdev.arcanumocculta.api.spell.AbstractSpell; +import app.simplexdev.arcanumocculta.api.spell.SpellEffect; +import app.simplexdev.arcanumocculta.api.spell.enums.Damages; +import app.simplexdev.arcanumocculta.api.spell.enums.Durations; +import app.simplexdev.arcanumocculta.api.spell.enums.ManaCosts; +import app.simplexdev.arcanumocculta.api.wand.Wand; +import app.simplexdev.arcanumocculta.util.SpellUtils; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.entity.Entity; + +public class WitherBurst extends AbstractSpell +{ + + protected WitherBurst() + { + super("Wither Burst", "wither_burst", + "Fire a bolt of withering energy at your enemies.", + CasterLevel.ADEPT, + Damages.SEVERE, + Durations.MEDIUM, + ManaCosts.HEAVY_CAST, + 120L); + } + + @Override + public SpellEffect[] getSpellEffects() + { + final SpellEffect[] effects = new SpellEffect[1]; + effects[0] = SpellUtils.witherEffectBase(baseDamage(), effectDuration()); + return effects; + } + + @Override + public void cast(Caster caster, Wand wand) + { + if (!checkManaCosts(caster)) + return; + + final Entity projectile = prepareProjectile(caster, + Material.AIR, + tracerVector(caster)); + + while (!projectile.isDead()) + { + tracerRGB(projectile.getWorld(), projectile.getLocation(), Particle.SPELL_MOB, 16, 16, 16); + + if (!projectile.getNearbyEntities(1, 1, 1).isEmpty()) + { + applyEffects(projectile.getNearbyEntities(1, 1, 1), caster); + projectile.remove(); + } + + if (projectile.isOnGround()) + { + projectile.remove(); + } + } + } +} diff --git a/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherMeteorite.java b/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherMeteorite.java index 0fd1c6f..0507e00 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherMeteorite.java +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherMeteorite.java @@ -32,10 +32,8 @@ import app.simplexdev.arcanumocculta.api.spell.enums.ManaCosts; import app.simplexdev.arcanumocculta.api.wand.Wand; import app.simplexdev.arcanumocculta.util.SpellUtils; import java.util.List; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Entity; -import org.bukkit.util.Vector; public final class WitherMeteorite extends AbstractSpell { @@ -63,16 +61,14 @@ public final class WitherMeteorite extends AbstractSpell @Override public void cast(Caster caster, Wand wand) { - if (this.checkManaCosts(caster)) + if (!checkManaCosts(caster)) return; - final Location location = caster.bukkit().getEyeLocation().clone().add(0, 256, 0); - final Vector direction = location.getDirection().add(new Vector(0, -5, 0)); final Entity projectile = prepareProjectile(caster, Material.WITHER_SKELETON_SKULL, - direction); + meteorVector(caster)); - getSpellEffects()[1] = SpellUtils.meteorLikeEffectBase(baseDamage(), direction, 4F); + getSpellEffects()[1] = SpellUtils.meteorLikeEffectBase(baseDamage(), meteorVector(caster), 4F); while (!projectile.isDead()) {