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 3bc3e4f..61a67ba 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/api/spell/AbstractSpell.java +++ b/src/main/java/app/simplexdev/arcanumocculta/api/spell/AbstractSpell.java @@ -14,12 +14,16 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.World; +import org.bukkit.entity.AreaEffectCloud; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.ItemDisplay; 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; public abstract class AbstractSpell implements Spell @@ -146,18 +150,21 @@ public abstract class AbstractSpell implements Spell .filter(LivingEntity.class::isInstance) .map(LivingEntity.class::cast) .forEach(entity -> applyEffectsIndividually( - getSpellEffects(), entity, - caster)); + caster, + getSpellEffects())); } - public Entity prepareProjectile(final Caster caster, final Material visual) + public void simulateExplosion(final Location location, final float size, boolean breakBlocks) { + location.getWorld().createExplosion(location, size, true, breakBlocks); + } + + public Entity prepareProjectile(final Caster caster, final Material visual, final Vector velocity) { final double expMod = getLevelRequirement().getExperienceMarker(); final Player player = caster.bukkit(); final Location location = player.getLocation().clone().add(0, player.getEyeHeight(), 0); - final Vector velocity = player.getLocation().getDirection().multiply(2); final Entity projectile = createProjectile(visual, player.getWorld(), location, velocity); caster.removeMana(manaCost().getManaCost()); @@ -175,8 +182,43 @@ public abstract class AbstractSpell implements Spell random().nextDouble(-2, 2)); } - private void applyEffectsIndividually(final SpellEffect[] effects, final LivingEntity target, - final Caster caster) + public void spiral(final World world, final Location location, final Particle particle) + { + final double step = 0.5; + final double radius = 2; + final double area = Math.PI * Math.pow(radius, 2); + final double theta = area / step; + final double phi = step / radius; + for (double i = 0; i < theta; i += phi) + { + final double x = radius * Math.cos(i); + final double z = radius * Math.sin(i); + world.spawnParticle(particle, location.clone().add(x, 0, z), 0); + } + } + + public AreaEffectCloud cloud(final World world, + 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) { + cloud.setBasePotionData(new PotionData(effect)); + } + + cloud.setRadius(size); + cloud.setRadiusPerTick(size / 0.5F); + + return cloud; + } + + protected void applyEffectsIndividually(final LivingEntity target, + final Caster caster, final SpellEffect... effects) { for (final SpellEffect effect : effects) effect.apply(target, caster); 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 042a51f..617d9ff 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 @@ -25,9 +25,9 @@ package app.simplexdev.arcanumocculta.api.spell.enums; // Reference for implementation. public enum SpellTypes { - SOUL_PEBBLE, SOUL_SHARD, LARGE_SOUL_SHARD, SOUL_COMET_SHARD, SOUL_COMET, SOUL_SHARD_SPIRAL, SOUL_STARS, - SOUL_STAR_SHOWER, SOUL_ARC, SOUL_BARRAGE, SOUL_BURST, SOUL_CANNON, SOUL_HAMMER, SOUL_BLAST, SOUL_EXPLOSION, - SOUL_LAMP, SOUL_PHALANX, GREAT_SOUL_PHALANX, SOUL_MIST, ARS_NOVA, ARS_ENIGMA, ARS_ULTIMA, + SOUL_PEBBLE, SOUL_SHARD, 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_ORB, FLAME_PILLARS, FLAME_WHIRLWIND, diff --git a/src/main/java/app/simplexdev/arcanumocculta/spells/flame/FlameSling.java b/src/main/java/app/simplexdev/arcanumocculta/spells/flame/FlameSling.java new file mode 100644 index 0000000..7563e9c --- /dev/null +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/flame/FlameSling.java @@ -0,0 +1,81 @@ +/* + * 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.flame; + +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.block.BlockFace; +import org.bukkit.entity.Entity; + +public class FlameSling extends AbstractSpell +{ + protected FlameSling() + { + super("Flame Sling", "flame_sling", + "Lob a ball of fire at your enemies!", + CasterLevel.APPRENTICE, + Damages.LIGHT, + Durations.MEDIUM, + ManaCosts.LIGHT_CAST, + 10L); + } + + @Override + public SpellEffect[] getSpellEffects() + { + final SpellEffect[] effects = new SpellEffect[1]; + effects[0] = SpellUtils.flameEffectBase(baseDamage()); + return effects; + } + + @Override + public void cast(Caster caster, Wand wand) + { + if (!checkManaCosts(caster)) + return; + + final Entity projectile = prepareProjectile(caster, Material.FIRE_CHARGE, + caster.bukkit() + .getLocation().clone() + .getDirection().multiply(2)); + + while (!projectile.isDead()) { + 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/soul/SoulPebble.java b/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulPebble.java index 7374475..9ca35f0 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulPebble.java +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulPebble.java @@ -43,7 +43,12 @@ public final class SoulPebble extends AbstractSpell return; } - final Entity projectile = prepareProjectile(caster, Material.AIR); + final Entity projectile = prepareProjectile(caster, Material.AIR, + caster.bukkit() + .getLocation() + .clone() + .getDirection() + .multiply(2)); while (!projectile.isOnGround() || !projectile.isDead()) { 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 1643a5f..92a7f69 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulShard.java +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/soul/SoulShard.java @@ -25,17 +25,15 @@ package app.simplexdev.arcanumocculta.spells.soul; 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.SpellEffect; 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; -import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffectType; public final class SoulShard extends AbstractSpell { @@ -65,10 +63,17 @@ public final class SoulShard extends AbstractSpell return; } - final Entity projectile = prepareProjectile(caster, Material.AIR); + final Entity projectile = prepareProjectile(caster, Material.AIR, + caster.bukkit() + .getLocation() + .clone() + .getDirection() + .multiply(2)); - while (!projectile.isDead()) { - for (int i = 0; i < 3; i++) { + while (!projectile.isDead()) + { + for (int i = 0; i < 3; i++) + { tracer(projectile.getWorld(), projectile.getLocation(), Particle.SOUL); } @@ -79,7 +84,8 @@ public final class SoulShard extends AbstractSpell projectile.remove(); } - if (projectile.isOnGround()) { + 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 new file mode 100644 index 0000000..0fd1c6f --- /dev/null +++ b/src/main/java/app/simplexdev/arcanumocculta/spells/wither/WitherMeteorite.java @@ -0,0 +1,93 @@ +/* + * 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 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 +{ + + public WitherMeteorite() + { + super("Wither Meteorite", + "wither_meteorite", + "Call down a wither skull from the heavens to wreak havoc on your enemies.", + CasterLevel.ARCH_MAGE, + Damages.OVERKILL, + Durations.MEDIUM, + ManaCosts.IMMENSE_CAST, + 600L); + } + + @Override + public SpellEffect[] getSpellEffects() + { + final SpellEffect[] effects = new SpellEffect[2]; + effects[0] = SpellUtils.witherEffectBase(baseDamage(), effectDuration()); + return effects; + } + + @Override + public void cast(Caster caster, Wand wand) + { + if (this.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); + + getSpellEffects()[1] = SpellUtils.meteorLikeEffectBase(baseDamage(), direction, 4F); + + while (!projectile.isDead()) + { + if (!projectile.getNearbyEntities(5, 5, 5).isEmpty()) + { + final List e = projectile.getNearbyEntities(5, 5, 5); + applyEffects(e, caster); + projectile.remove(); + } + + if (projectile.isOnGround()) + { + simulateExplosion(projectile.getLocation(), 4F, true); + projectile.remove(); + } + } + } +} diff --git a/src/main/java/app/simplexdev/arcanumocculta/util/SpellUtils.java b/src/main/java/app/simplexdev/arcanumocculta/util/SpellUtils.java index 8d4716c..f9d954f 100644 --- a/src/main/java/app/simplexdev/arcanumocculta/util/SpellUtils.java +++ b/src/main/java/app/simplexdev/arcanumocculta/util/SpellUtils.java @@ -25,6 +25,7 @@ package app.simplexdev.arcanumocculta.util; import app.simplexdev.arcanumocculta.api.spell.Spell; 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.spells.PrimarySpellList; import java.util.SplittableRandom; import org.bukkit.Particle; @@ -83,13 +84,13 @@ public final class SpellUtils }; } - public static SpellEffect witherEffectBase(final Damages baseDamage) + public static SpellEffect witherEffectBase(final Damages baseDamage, final Durations duration) { return (target, caster) -> { final var damage = baseDamage.multiply(caster.getWand().getSpellBonus()); // Wither for 10 seconds. - target.addPotionEffect(PotionEffectType.WITHER.createEffect(200, 2)); + target.addPotionEffect(PotionEffectType.WITHER.createEffect((int) duration.getTicks(), 3)); damage(target, caster.bukkit(), damage); };