Wonderful API!

Now for implementations 😧
This commit is contained in:
Paul Reilly 2023-07-18 17:55:55 -05:00
parent 893ed446c4
commit bc4f6c033f
4 changed files with 239 additions and 65 deletions

View File

@ -5,8 +5,22 @@ import app.simplexdev.arcanumocculta.api.caster.CasterLevel;
import app.simplexdev.arcanumocculta.api.spell.enums.Damages; import app.simplexdev.arcanumocculta.api.spell.enums.Damages;
import app.simplexdev.arcanumocculta.api.spell.enums.Durations; import app.simplexdev.arcanumocculta.api.spell.enums.Durations;
import app.simplexdev.arcanumocculta.api.spell.enums.ManaCosts; import app.simplexdev.arcanumocculta.api.spell.enums.ManaCosts;
import app.simplexdev.arcanumocculta.util.SpellUtils;
import java.util.List;
import java.util.SplittableRandom; import java.util.SplittableRandom;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.World;
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.util.Vector;
public abstract class AbstractSpell implements Spell public abstract class AbstractSpell implements Spell
{ {
@ -110,4 +124,75 @@ public abstract class AbstractSpell implements Spell
{ {
return random; return random;
} }
/**
* Checks if the caster has enough mana to cast this spell.
*
* @param caster The caster who is casting the spell
* @return True if the caster has enough mana, false otherwise
*/
protected boolean checkManaCosts(final Caster caster)
{
boolean isValid = !manaCost().isMoreThan(caster.getCurrentMana());
if (!isValid)
caster.bukkit().sendMessage(ChatColor.RED + "You don't have enough mana to cast this spell!");
return isValid;
}
public void applyEffects(final List<Entity> targets, final Caster caster)
{
targets.stream()
.filter(LivingEntity.class::isInstance)
.map(LivingEntity.class::cast)
.forEach(entity -> applyEffectsIndividually(
getSpellEffects(),
entity,
caster));
}
public Entity prepareProjectile(final Caster caster, final Material visual)
{
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());
caster.addExperience(random().nextDouble(expMod * 0.25));
return projectile;
}
public void tracer(final World world, final Location location, final Particle particle)
{
world.spawnParticle(particle,
location,
0,
random().nextDouble(-2, 2),
random().nextDouble(-2, 2),
random().nextDouble(-2, 2));
}
private void applyEffectsIndividually(final SpellEffect[] effects, final LivingEntity target,
final Caster caster)
{
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;
}
} }

View File

@ -3,19 +3,15 @@ package app.simplexdev.arcanumocculta.spells.soul;
import app.simplexdev.arcanumocculta.api.caster.Caster; import app.simplexdev.arcanumocculta.api.caster.Caster;
import app.simplexdev.arcanumocculta.api.caster.CasterLevel; import app.simplexdev.arcanumocculta.api.caster.CasterLevel;
import app.simplexdev.arcanumocculta.api.spell.AbstractSpell; 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.Damages;
import app.simplexdev.arcanumocculta.api.spell.enums.Durations; 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.spell.enums.ManaCosts;
import app.simplexdev.arcanumocculta.api.wand.Wand; import app.simplexdev.arcanumocculta.api.wand.Wand;
import app.simplexdev.arcanumocculta.util.SpellUtils; import app.simplexdev.arcanumocculta.util.SpellUtils;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Particle; import org.bukkit.Particle;
import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
public final class SoulPebble extends AbstractSpell public final class SoulPebble extends AbstractSpell
{ {
@ -35,56 +31,33 @@ public final class SoulPebble extends AbstractSpell
public SpellEffect[] getSpellEffects() public SpellEffect[] getSpellEffects()
{ {
final SpellEffect[] effects = new SpellEffect[1]; final SpellEffect[] effects = new SpellEffect[1];
effects[0] = (target, caster) -> effects[0] = SpellUtils.soulEffectBase(baseDamage());
{
final Wand wand = caster.getWand();
final double damage = baseDamage().multiply(wand.getSpellBonus());
SpellUtils.damage(target, caster.bukkit(), damage);
};
return effects; return effects;
} }
@Override @Override
public void cast(Caster caster, Wand wand) public void cast(Caster caster, Wand wand)
{ {
if (this.manaCost().isMoreThan(caster.getCurrentMana())) if (!this.checkManaCosts(caster))
{ {
caster.bukkit().sendMessage("You do not have enough mana to cast this spell!");
return; return;
} }
final double expMod = this.getLevelRequirement().getExperienceMarker(); final Entity projectile = prepareProjectile(caster, Material.AIR);
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 ItemDisplay projectile = SpellUtils.createProjectile(Material.AIR, player.getWorld(), location,
velocity);
caster.removeMana(this.manaCost().getManaCost());
caster.addExperience(random().nextDouble(expMod * 0.25));
while (!projectile.isOnGround() || !projectile.isDead()) while (!projectile.isOnGround() || !projectile.isDead())
{ {
projectile.getWorld().spawnParticle(Particle.SOUL, projectile.getLocation(), 1, tracer(projectile.getWorld(), projectile.getLocation(), Particle.SOUL);
random().nextGaussian() * 0.2,
random().nextGaussian() * 0.2,
random().nextGaussian() * 0.2);
if (!projectile.getNearbyEntities(1, 1, 1).isEmpty()) { if (!projectile.getNearbyEntities(1, 1, 1).isEmpty())
projectile.getNearbyEntities(1, 1, 1) {
.stream() applyEffects(projectile.getNearbyEntities(1, 1, 1),
.filter(LivingEntity.class::isInstance) caster);
.map(LivingEntity.class::cast)
.forEach(entity -> SpellUtils.applyEffects(
this.getSpellEffects(),
entity,
caster));
projectile.remove(); projectile.remove();
break;
}
} }
if (projectile.isOnGround()) projectile.remove(); if (projectile.isOnGround())
projectile.remove();
}
} }
} }

View File

@ -30,6 +30,12 @@ import app.simplexdev.arcanumocculta.api.spell.enums.Durations;
import app.simplexdev.arcanumocculta.api.spell.SpellEffect; import app.simplexdev.arcanumocculta.api.spell.SpellEffect;
import app.simplexdev.arcanumocculta.api.spell.enums.ManaCosts; import app.simplexdev.arcanumocculta.api.spell.enums.ManaCosts;
import app.simplexdev.arcanumocculta.api.wand.Wand; 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 public final class SoulShard extends AbstractSpell
{ {
@ -46,12 +52,36 @@ public final class SoulShard extends AbstractSpell
@Override @Override
public SpellEffect[] getSpellEffects() public SpellEffect[] getSpellEffects()
{ {
return new SpellEffect[0]; final SpellEffect[] effects = new SpellEffect[1];
effects[0] = SpellUtils.soulEffectBase(baseDamage());
return effects;
} }
@Override @Override
public void cast(Caster caster, Wand wand) public void cast(Caster caster, Wand wand)
{ {
if (!this.checkManaCosts(caster))
{
return;
}
final Entity projectile = prepareProjectile(caster, Material.AIR);
while (!projectile.isDead()) {
for (int i = 0; i < 3; i++) {
tracer(projectile.getWorld(), projectile.getLocation(), Particle.SOUL);
}
if (!projectile.getNearbyEntities(1, 1, 1).isEmpty())
{
applyEffects(projectile.getNearbyEntities(1, 1, 1),
caster);
projectile.remove();
}
if (projectile.isOnGround()) {
projectile.remove();
}
}
} }
} }

View File

@ -22,25 +22,23 @@
package app.simplexdev.arcanumocculta.util; package app.simplexdev.arcanumocculta.util;
import app.simplexdev.arcanumocculta.api.caster.Caster;
import app.simplexdev.arcanumocculta.api.spell.Spell; import app.simplexdev.arcanumocculta.api.spell.Spell;
import app.simplexdev.arcanumocculta.api.spell.SpellEffect; import app.simplexdev.arcanumocculta.api.spell.SpellEffect;
import app.simplexdev.arcanumocculta.api.spell.enums.Damages;
import app.simplexdev.arcanumocculta.spells.PrimarySpellList; import app.simplexdev.arcanumocculta.spells.PrimarySpellList;
import org.bukkit.Location; import java.util.SplittableRandom;
import org.bukkit.Material; import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.entity.Damageable; import org.bukkit.entity.Damageable;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ItemDisplay;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
public final class SpellUtils public final class SpellUtils
{ {
public static final String SPELL_PACKAGE = PrimarySpellList.class.getPackageName(); public static final String SPELL_PACKAGE = PrimarySpellList.class.getPackageName();
private static final PrimarySpellList primarySpellList = new PrimarySpellList(); private static final PrimarySpellList primarySpellList = new PrimarySpellList();
private static final SplittableRandom RANDOM = new SplittableRandom();
private SpellUtils() private SpellUtils()
{ {
@ -55,28 +53,116 @@ public final class SpellUtils
target.damage(damage); target.damage(damage);
} }
public static void applyEffects(final SpellEffect[] effects, final LivingEntity target, final Caster caster)
{
for (final SpellEffect effect : effects)
effect.apply(target, caster);
}
public static Spell copyFromPrimaryList(final String id) public static Spell copyFromPrimaryList(final String id)
{ {
return SpellUtils.primarySpellList.getSpell(id).dupe(); return SpellUtils.primarySpellList.getSpell(id).dupe();
} }
public static ItemDisplay createProjectile(final Material visual, final World world, final Location location, public static SpellEffect soulEffectBase(final Damages baseDamage)
final Vector velocity)
{ {
final ItemDisplay display = (ItemDisplay) world.spawnEntity(location, EntityType.ITEM_DISPLAY); return (target, caster) ->
display.setItemStack(new ItemStack(visual)); {
display.setGravity(true); final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
display.setPersistent(false); if (target instanceof Player player)
display.setSilent(true); // Player is freezing when hit, as if they were in powdered snow for 1 minute.
display.setShadowRadius(0); player.setFreezeTicks(1200);
display.setShadowStrength(0);
display.setVelocity(velocity); damage(target, caster.bukkit(), damage);
return display; };
}
public static SpellEffect flameEffectBase(final Damages baseDamage)
{
return (target, caster) ->
{
final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
// Fire ticks for 15 seconds.
target.setFireTicks(300);
damage(target, caster.bukkit(), damage);
};
}
public static SpellEffect witherEffectBase(final Damages baseDamage)
{
return (target, caster) ->
{
final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
// Wither for 10 seconds.
target.addPotionEffect(PotionEffectType.WITHER.createEffect(200, 2));
damage(target, caster.bukkit(), damage);
};
}
public static SpellEffect lightningEffectBase(final Damages baseDamage)
{
return (target, caster) ->
{
final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
// Lightning on target
target.getWorld().strikeLightning(target.getLocation());
damage(target, caster.bukkit(), damage);
};
}
public static SpellEffect healEffectBase(final Damages baseDamage)
{
return (target, caster) ->
{
final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
// Heal target
target.setHealth(target.getHealth() + damage);
};
}
public static SpellEffect regenEffectBase(final Damages baseDamage)
{
return (target, caster) ->
{
final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
// Regen target
target.addPotionEffect(PotionEffectType.REGENERATION.createEffect(300, 3));
target.setHealth(target.getHealth() + ((target.getHealth() + damage) / 2));
};
}
public static SpellEffect meteorLikeEffectBase(final Damages baseDamage,
final Vector incomingVelocity,
final float size)
{
return (target, caster) ->
{
final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
// Meteor like effect
// TODO: Make a configuration option to enable/disable explosions destroying blocks.
target.getWorld().createExplosion(target.getLocation(), size, true, false);
target.setVelocity(incomingVelocity.normalize().multiply(2).multiply(-1));
damage(target, caster.bukkit(), damage);
};
}
public static SpellEffect arsLikeEffectBase(final Damages baseDamage)
{
return (target, caster) ->
{
final var damage = baseDamage.multiply(caster.getWand().getSpellBonus());
// Ars Magica like effect
target.setVelocity(caster.bukkit().getLocation().clone().getDirection().multiply(2));
if (target instanceof Player player)
player.setFreezeTicks(1200);
for (int i = 0; i < 50; i++)
{
target.getWorld().spawnParticle(Particle.SOUL_FIRE_FLAME,
target.getLocation(),
0,
RANDOM.nextDouble(-2, 2),
RANDOM.nextDouble(-2, 2),
RANDOM.nextDouble(-2, 2));
}
damage(target, caster.bukkit(), damage);
};
} }
} }