diff --git a/pom.xml b/pom.xml
index 8ab2841..e595823 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,5 +70,10 @@
1.19.4-R0.1-SNAPSHOT
provided
+
+ redis.clients
+ jedis
+ 4.3.1
+
diff --git a/src/main/java/me/sticksdev/runicspells/Runic_spells.java b/src/main/java/me/sticksdev/runicspells/Runic_spells.java
index 62e4598..17e5dd9 100644
--- a/src/main/java/me/sticksdev/runicspells/Runic_spells.java
+++ b/src/main/java/me/sticksdev/runicspells/Runic_spells.java
@@ -1,12 +1,18 @@
package me.sticksdev.runicspells;
+import me.sticksdev.runicspells.handlers.CooldownHandler;
+import me.sticksdev.runicspells.handlers.ManaHandler;
import me.sticksdev.runicspells.handlers.SpellHandler;
+import me.sticksdev.runicspells.utils.Redis;
import me.sticksdev.runicspells.utils.Yaml;
import org.bukkit.plugin.java.JavaPlugin;
public final class Runic_spells extends JavaPlugin {
public static Runic_spells instance;
- private final Yaml config = new Yaml(this); // TODO: Maybe make this use instance instead of this - was dealing with a null pointer exception
+ private static Redis redis;
+ private static Yaml config;
+ private static CooldownHandler cooldownHandler;
+ private static ManaHandler manaHandler;
@Override
public void onEnable() {
@@ -14,8 +20,23 @@ public final class Runic_spells extends JavaPlugin {
instance = this;
getLogger().info("Runic Spells has been enabled!");
getLogger().info("Loading config...");
+
+ // Load config
+ config = new Yaml();
config.init();
+ // Init Redis
+ redis = new Redis();
+ redis.init();
+
+ // Init cooldown handler
+ cooldownHandler = new CooldownHandler();
+
+ // Register mana handler
+ manaHandler = new ManaHandler();
+ manaHandler.startTimers();
+ getServer().getPluginManager().registerEvents(manaHandler, this);
+
// Register spells
SpellHandler.registerSpells();
}
@@ -23,9 +44,27 @@ public final class Runic_spells extends JavaPlugin {
@Override
public void onDisable() {
config.destroy();
+ redis.close();
+ manaHandler.destroyTimers();
}
public static Runic_spells getInstance() {
return instance;
}
+
+ public Yaml getConfigHandler() {
+ return config;
+ }
+
+ public Redis getRedisHandler() {
+ return redis;
+ }
+
+ public CooldownHandler getCooldownHandler() {
+ return cooldownHandler;
+ }
+
+ public ManaHandler getManaHandler() {
+ return manaHandler;
+ }
}
diff --git a/src/main/java/me/sticksdev/runicspells/handlers/CooldownHandler.java b/src/main/java/me/sticksdev/runicspells/handlers/CooldownHandler.java
index ba12078..cdc93d5 100644
--- a/src/main/java/me/sticksdev/runicspells/handlers/CooldownHandler.java
+++ b/src/main/java/me/sticksdev/runicspells/handlers/CooldownHandler.java
@@ -1,8 +1,42 @@
package me.sticksdev.runicspells.handlers;
+
+import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.structures.ItemBasedSpell;
+import me.sticksdev.runicspells.utils.Redis;
import org.bukkit.entity.Player;
+import redis.clients.jedis.JedisPooled;
+import redis.clients.jedis.params.SetParams;
import java.util.HashMap;
public class CooldownHandler {
+ private final Redis redis = Runic_spells.getInstance().getRedisHandler();
+ private final JedisPooled pool = redis.getPool();
+ // Note: Java doesn't support union types for parameters, so we explicitly define the type of the spell
+ // Even though it *technically* could be a BaseSpell, it's not worth the hassle of casting it
+ public void setCooldown(Player player, ItemBasedSpell spell, int cooldown) {
+ pool.set(player.getUniqueId().toString() + ":" + spell.getSpellID() + ":cooldown", String.valueOf(cooldown), new SetParams().ex(cooldown));
+ }
+
+ public double getCooldown(Player player, ItemBasedSpell spell) {
+ String key = player.getUniqueId().toString() + ":" + spell.getSpellID() + ":cooldown";
+ String cooldown = pool.get(key);
+
+ // Check if the key exists in Redis
+ if (cooldown == null) {
+ return 0; // No cooldown
+ }
+
+ // Get the remaining time to live in seconds
+ long ttl = pool.ttl(key);
+
+ // Check if the object has expired
+ if (ttl < 0) {
+ return 0; // Object has expired, so no cooldown
+ }
+
+ // Return the time left before the object is gone
+ return (double) ttl;
+ }
}
diff --git a/src/main/java/me/sticksdev/runicspells/handlers/ManaHandler.java b/src/main/java/me/sticksdev/runicspells/handlers/ManaHandler.java
index 03f1f20..9d480ff 100644
--- a/src/main/java/me/sticksdev/runicspells/handlers/ManaHandler.java
+++ b/src/main/java/me/sticksdev/runicspells/handlers/ManaHandler.java
@@ -1,4 +1,115 @@
package me.sticksdev.runicspells.handlers;
-public class ManaHandler {
+import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.structures.ItemBasedSpell;
+import me.sticksdev.runicspells.utils.Redis;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextDecoration;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.scheduler.BukkitRunnable;
+import redis.clients.jedis.JedisPooled;
+
+public class ManaHandler implements Listener {
+ private final Redis redis = Runic_spells.getInstance().getRedisHandler();
+ private final JedisPooled pool = redis.getPool();
+ private BukkitRunnable manaTimer;
+
+ public void setMana(String uuid, int mana) {
+ pool.set(uuid + ":mana", String.valueOf(mana));
+ }
+
+ public void startTimers() {
+ manaTimer = new BukkitRunnable() {
+ @Override
+ public void run() {
+ for (Player player : Runic_spells.getInstance().getServer().getOnlinePlayers()) {
+ int mana = getMana(player.getUniqueId().toString());
+ if (mana < 100) {
+ // Give an amount between 1 and 5
+ int amount = (int) (Math.random() * 5 + 1);
+ int newMana = mana + amount;
+
+ // Ensure they don't go over 100
+ if (newMana > 100) {
+ setMana(player.getUniqueId().toString(), 100);
+ } else {
+ addMana(player, amount);
+ }
+
+ // If the new mana is either equal or greater than 100, send a message to the player
+ if (newMana >= 100) {
+ // Send a message to the player saying they're at max mana
+ final Component message = Component.text()
+ .append(Component.text("[RS]", NamedTextColor.GREEN, TextDecoration.BOLD))
+ .append(Component.text(" You are at max mana!", NamedTextColor.GRAY))
+ .build();
+ player.sendMessage(message);
+ }
+ }
+ }
+ }
+ };
+ manaTimer.runTaskTimer(Runic_spells.getInstance(), 0, 20);
+ }
+
+ public void destroyTimers() {
+ manaTimer.cancel();
+ }
+
+ public int getMana(String uuid) {
+ String mana = pool.get(uuid + ":mana");
+
+ if (mana == null) {
+ return 0;
+ }
+
+ return Integer.parseInt(mana);
+ }
+
+ public void addMana(Player player, int mana) {
+ String uuid = player.getUniqueId().toString();
+ int currentMana = getMana(uuid);
+ setMana(uuid, currentMana + mana);
+ setEXPBar(player);
+ }
+
+ public void removeMana(Player player, int mana) {
+ String uuid = player.getUniqueId().toString();
+ int currentMana = getMana(uuid);
+ setMana(uuid, currentMana - mana);
+ setEXPBar(player);
+ }
+
+
+ public void setEXPBar(Player player) {
+ int mana = getMana(player.getUniqueId().toString());
+ player.setExp((float) mana / 100);
+ }
+
+ public void refundSpell(Player player, ItemBasedSpell spell) {
+ addMana(player, spell.getManaCost());
+ setEXPBar(player);
+ }
+
+ public boolean canCast(Player player, int manaCost) {
+ return getMana(player.getUniqueId().toString()) >= manaCost;
+ }
+
+ // On player join
+ @EventHandler
+ public void onPlayerJoin(PlayerJoinEvent event) {
+ boolean exists = pool.exists(event.getPlayer().getUniqueId() + ":mana");
+ Player player = event.getPlayer();
+
+ if (!exists) {
+ setMana(player.getUniqueId().toString(), 100);
+ setEXPBar(player);
+ } else {
+ setEXPBar(player);
+ }
+ }
}
diff --git a/src/main/java/me/sticksdev/runicspells/handlers/SpellHandler.java b/src/main/java/me/sticksdev/runicspells/handlers/SpellHandler.java
index 1fd67a6..5f01eb9 100644
--- a/src/main/java/me/sticksdev/runicspells/handlers/SpellHandler.java
+++ b/src/main/java/me/sticksdev/runicspells/handlers/SpellHandler.java
@@ -1,7 +1,13 @@
package me.sticksdev.runicspells.handlers;
import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.spells.EarthSpell;
import me.sticksdev.runicspells.spells.FireSpell;
+import me.sticksdev.runicspells.spells.LightningSpell;
+import me.sticksdev.runicspells.spells.WaterSpell;
import me.sticksdev.runicspells.structures.ItemBasedSpell;
+import me.sticksdev.runicspells.structures.SpellOverride;
+import me.sticksdev.runicspells.utils.Logger;
+import me.sticksdev.runicspells.utils.Yaml;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.Material;
@@ -10,31 +16,49 @@ import java.util.HashMap;
public class SpellHandler {
public static HashMap itemBasedSpells = new HashMap<>();
+ private static final Yaml config = Runic_spells.getInstance().getConfigHandler();
+ private static final Runic_spells plugin = Runic_spells.getInstance();
+
public static void registerItemSpell(ItemBasedSpell spell) {
// Check if the item provided is a valid material
try {
Material itemMaterial = Material.valueOf(spell.getItem());
} catch (IllegalArgumentException e) {
- Runic_spells.getInstance().getLogger().warning("Invalid item material provided for spell " + spell.getName() + "!");
- Runic_spells.getInstance().getLogger().warning("Item material provided: " + spell.getItem());
+ Logger.warning("Spell " + spell.getName() + " has an invalid item material!");
+ Logger.warning("Provided material: " + spell.getItem());
return;
}
// Check if the spell is already registered
if (itemBasedSpells.containsKey(spell.getName())) {
- Runic_spells.getInstance().getLogger().warning("Spell " + spell.getName() + " is already registered!");
+ Logger.warning("Spell " + spell.getName() + " is already registered!");
return;
}
+ // Check if the spell is enabled
+ boolean spellEnabled = config.getIsSpellEnabled(spell.getName());
+ if (!spellEnabled) {
+ Logger.warning("Spell " + spell.getName() + " is disabled in the config - not registering!");
+ return;
+ }
+
+ // Check if any overrides are present
+ SpellOverride override = config.getSpellOverrides(spell.getName());
+
+ if(override != null) {
+ spell.setOverrides(override);
+ Logger.info("Spell " + spell.getName() + " overrides set!");
+ }
+
// Listen for "use" on that item
- Runic_spells.getInstance().getServer().getPluginManager().registerEvents(spell, Runic_spells.getInstance());
+ plugin.getServer().getPluginManager().registerEvents(spell, plugin);
// Register the spell
itemBasedSpells.put(spell.getName(), spell);
// Log the spell registration
- Runic_spells.getInstance().getLogger().info("Registered spell " + spell.getName() + "!");
+ Logger.info("Registered spell " + spell.getName() + "!");
}
public static ItemBasedSpell getItemSpell(String name) {
@@ -50,7 +74,11 @@ public class SpellHandler {
public static void registerSpells() {
// Register spells
- Runic_spells.getInstance().getLogger().info("Registering spells...");
+ Logger.info("Registering spells...");
registerItemSpell(new FireSpell());
+ registerItemSpell(new WaterSpell());
+ registerItemSpell(new EarthSpell());
+ registerItemSpell(new LightningSpell());
+ Logger.info("Registered " + itemBasedSpells.size() + " spells!");
}
}
diff --git a/src/main/java/me/sticksdev/runicspells/spells/EarthSpell.java b/src/main/java/me/sticksdev/runicspells/spells/EarthSpell.java
new file mode 100644
index 0000000..81cab54
--- /dev/null
+++ b/src/main/java/me/sticksdev/runicspells/spells/EarthSpell.java
@@ -0,0 +1,56 @@
+package me.sticksdev.runicspells.spells;
+
+import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.structures.ItemBasedSpell;
+import me.sticksdev.runicspells.utils.Utils;
+import org.bukkit.entity.*;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+
+public class EarthSpell extends ItemBasedSpell {
+ public EarthSpell() {
+ super("Earth", "Launches an earth projectile", 2, "DIRT", 25, 40, 15, EarthSpell::castEarth);
+ }
+
+ private static void castEarth(Player player, Entity nearestEntity) {
+ // Launches an earth projectile
+ Projectile earth = player.launchProjectile(Snowball.class, Utils.getProjectileVelocity(player, nearestEntity));
+
+ // Wait for it to hit something
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (earth.isDead() || earth.isOnGround()) {
+ // Get the location where the earth hit
+ Location impactLocation = earth.getLocation();
+
+ // Spawn earth around the impact location
+ World world = impactLocation.getWorld();
+ if (world != null) {
+ for (int x = -2; x <= 2; x++) {
+ for (int y = -2; y <= 2; y++) {
+ for (int z = -2; z <= 2; z++) {
+ Location blockLocation = impactLocation.clone().add(x, y, z);
+ Material blockType = blockLocation.getBlock().getType();
+
+ // Check if the block is not air or bedrock
+ if (blockType != Material.AIR && blockType != Material.BEDROCK) {
+ TNTPrimed tnt = world.spawn(blockLocation, TNTPrimed.class);
+
+ // Set the fuse ticks to 40 (2 seconds)
+ tnt.setFuseTicks(40);
+ }
+ }
+ }
+ }
+ }
+
+ // Cancel the task
+ cancel();
+ }
+ }
+ }.runTaskTimer(Runic_spells.getInstance(), 0L, 1L);
+ }
+}
diff --git a/src/main/java/me/sticksdev/runicspells/spells/FireSpell.java b/src/main/java/me/sticksdev/runicspells/spells/FireSpell.java
index d940ed2..f18b48e 100644
--- a/src/main/java/me/sticksdev/runicspells/spells/FireSpell.java
+++ b/src/main/java/me/sticksdev/runicspells/spells/FireSpell.java
@@ -2,6 +2,7 @@ package me.sticksdev.runicspells.spells;
import me.sticksdev.runicspells.Runic_spells;
import me.sticksdev.runicspells.structures.ItemBasedSpell;
+import me.sticksdev.runicspells.utils.Utils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
@@ -14,12 +15,12 @@ import org.bukkit.util.Vector;
public class FireSpell extends ItemBasedSpell {
public FireSpell() {
- super("Fire Spell", "Launches a fireball projectile", "FIRE_CHARGE", 10, 25, 15, FireSpell::castFireball);
+ super("Fireball", "Launches a fireball projectile", 1, "FIRE_CHARGE", 6, 25, 15, FireSpell::castFireball);
}
private static void castFireball(Player player, Entity nearestEntity) {
// Launches a fireball projectile
- Projectile fireball = player.launchProjectile(Snowball.class, getProjectileVelocity(player, nearestEntity));
+ Projectile fireball = player.launchProjectile(Snowball.class, Utils.getProjectileVelocity(player, nearestEntity));
// Set the fireball's damage
fireball.setFireTicks(100);
@@ -56,15 +57,4 @@ public class FireSpell extends ItemBasedSpell {
}
}.runTaskTimer(Runic_spells.getInstance(), 0L, 1L);
}
-
-
- private static Vector getProjectileVelocity(Player player, Entity target) {
- Vector direction;
- if (target != null) {
- direction = target.getLocation().toVector().subtract(player.getLocation().toVector());
- } else {
- direction = player.getEyeLocation().getDirection();
- }
- return direction.normalize().multiply(1.5);
- }
}
diff --git a/src/main/java/me/sticksdev/runicspells/spells/LightningSpell.java b/src/main/java/me/sticksdev/runicspells/spells/LightningSpell.java
new file mode 100644
index 0000000..d5b79b3
--- /dev/null
+++ b/src/main/java/me/sticksdev/runicspells/spells/LightningSpell.java
@@ -0,0 +1,33 @@
+package me.sticksdev.runicspells.spells;
+
+import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.structures.ItemBasedSpell;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextDecoration;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+
+public class LightningSpell extends ItemBasedSpell {
+ public LightningSpell() {
+ super("Lightning", "Strikes lightning at the target", 4, "BLAZE_ROD", 3, 10, 0, LightningSpell::castLightning);
+ }
+
+ private static void castLightning(Player player, Entity nearestEntity) {
+ if (player.getTargetBlock(null, 100).getType().isAir()) {
+ // Strike lightning at the player
+ player.getWorld().strikeLightning(player.getTargetBlock(null, 100).getLocation());
+ } else if(nearestEntity != null) {
+ // Strike lightning at the nearest entity
+ player.getWorld().strikeLightning(nearestEntity.getLocation());
+ } else {
+ final TextComponent errorMessage = Component.text()
+ .append(Component.text("[!]", NamedTextColor.RED, TextDecoration.BOLD))
+ .append(Component.text(" You must be near an entity/mob or player to cast this spell!", NamedTextColor.RED))
+ .build();
+
+ player.sendMessage(errorMessage);
+ }
+ }
+}
diff --git a/src/main/java/me/sticksdev/runicspells/spells/WaterSpell.java b/src/main/java/me/sticksdev/runicspells/spells/WaterSpell.java
new file mode 100644
index 0000000..d6d76a6
--- /dev/null
+++ b/src/main/java/me/sticksdev/runicspells/spells/WaterSpell.java
@@ -0,0 +1,56 @@
+package me.sticksdev.runicspells.spells;
+
+import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.structures.ItemBasedSpell;
+import me.sticksdev.runicspells.utils.Utils;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.entity.Snowball;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+
+public class WaterSpell extends ItemBasedSpell {
+ public WaterSpell() {
+ super("Water", "Launches a water projectile", 3, "WATER_BUCKET", 3, 10, 0, WaterSpell::castWater);
+ }
+
+ private static void castWater(Player player, Entity nearestEntity) {
+ // Launches a water projectile
+ Projectile water = player.launchProjectile(Snowball.class, Utils.getProjectileVelocity(player, nearestEntity));
+
+ // Wait for it to hit something
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ if (water.isDead() || water.isOnGround()) {
+ // Get the location where the water hit
+ Location impactLocation = water.getLocation();
+
+ // Spawn water around the impact location
+ World world = impactLocation.getWorld();
+ if (world != null) {
+ for (int x = -2; x <= 2; x++) {
+ for (int y = -2; y <= 2; y++) {
+ for (int z = -2; z <= 2; z++) {
+ Location waterLocation = impactLocation.clone().add(x, y, z);
+ if (waterLocation.getBlock().getType() == Material.AIR) {
+ waterLocation.getBlock().setType(Material.WATER);
+ }
+
+ // Add explosion effect
+ world.createExplosion(waterLocation, 0.0F, false);
+ }
+ }
+ }
+ }
+
+ // Cancel the task
+ cancel();
+ }
+ }
+ }.runTaskTimer(Runic_spells.getInstance(), 0L, 1L);
+ }
+}
diff --git a/src/main/java/me/sticksdev/runicspells/structures/BaseSpell.java b/src/main/java/me/sticksdev/runicspells/structures/BaseSpell.java
index 2af4d82..35bbd63 100644
--- a/src/main/java/me/sticksdev/runicspells/structures/BaseSpell.java
+++ b/src/main/java/me/sticksdev/runicspells/structures/BaseSpell.java
@@ -6,10 +6,12 @@ package me.sticksdev.runicspells.structures;
public class BaseSpell {
public String name;
public String description;
+ public int spellID;
- public BaseSpell(String name, String description) {
+ public BaseSpell(String name, String description, int spellID) {
this.name = name;
this.description = description;
+ this.spellID = spellID;
}
public String getName() {
@@ -19,4 +21,8 @@ public class BaseSpell {
public String getDescription() {
return description;
}
+
+ public int getSpellID() {
+ return spellID;
+ }
}
diff --git a/src/main/java/me/sticksdev/runicspells/structures/ItemBasedSpell.java b/src/main/java/me/sticksdev/runicspells/structures/ItemBasedSpell.java
index ee6504d..d2802aa 100644
--- a/src/main/java/me/sticksdev/runicspells/structures/ItemBasedSpell.java
+++ b/src/main/java/me/sticksdev/runicspells/structures/ItemBasedSpell.java
@@ -1,10 +1,21 @@
package me.sticksdev.runicspells.structures;
+import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.handlers.CooldownHandler;
+import me.sticksdev.runicspells.handlers.ManaHandler;
+import me.sticksdev.runicspells.utils.Logger;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextDecoration;
+import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
+import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer;
@@ -15,9 +26,11 @@ public class ItemBasedSpell extends BaseSpell implements Listener {
private int manaCost;
private int damage;
private final BiConsumer castHandler;
+ private final CooldownHandler cooldownHandler = Runic_spells.getInstance().getCooldownHandler();
+ private final ManaHandler manaHandler = Runic_spells.getInstance().getManaHandler();
- public ItemBasedSpell(String name, String description, String item, int cooldown, int manaCost, int damage, BiConsumer castHandler) {
- super(name, description);
+ public ItemBasedSpell(String name, String description, int spellId, String item, int cooldown, int manaCost, int damage, BiConsumer castHandler) {
+ super(name, description, spellId);
this.name = name;
this.description = description;
this.item = item;
@@ -68,9 +81,91 @@ public class ItemBasedSpell extends BaseSpell implements Listener {
// Listen to the interact event for only the item specified
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
- if (event.getItem() != null && event.getItem().getType().toString().equals(item)) {
- // For now, just cast the spell on the nearest entity
- cast(event.getPlayer(), event.getPlayer().getNearbyEntities(5, 5, 5).stream().findFirst().orElse(null));
+ // TODO: Little messy, could clean this up later
+ if (event.getItem() != null && event.getItem().getType() != Material.AIR &&
+ event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.LEFT_CLICK_BLOCK &&
+ (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_AIR)) {
+ if (event.getItem() != null && event.getItem().getType().toString().equals(item)) {
+ // Check if they have an active cooldown
+ Player player = event.getPlayer();
+ double cooldown = cooldownHandler.getCooldown(player, this);
+
+ if (cooldown == 0) {
+ // No cooldown check if they have enough mana
+ boolean canCast = manaHandler.canCast(player, this.manaCost);
+ if (canCast) {
+ // They have enough mana, so cast the spell
+ cast(player, event.getPlayer().getNearbyEntities(10, 10, 10).stream().findFirst().orElse(null));
+ cooldownHandler.setCooldown(player, this, this.cooldown);
+ manaHandler.removeMana(player, this.manaCost);
+
+ final TextComponent manaMessage = Component.text()
+ .append(Component.text("[RS]", NamedTextColor.GREEN, TextDecoration.BOLD))
+ .append(Component.text(" You cast "))
+ .append(Component.text(this.name, NamedTextColor.BLUE, TextDecoration.BOLD))
+ .append(Component.text(" for "))
+ .append(Component.text(this.manaCost, NamedTextColor.BLUE, TextDecoration.BOLD))
+ .append(Component.text(" mana."))
+ .build();
+
+ player.sendMessage(manaMessage);
+ } else {
+ // They don't have enough mana, so tell them
+ final TextComponent manaMessage = Component.text()
+ .append(Component.text("[!]", NamedTextColor.RED, TextDecoration.BOLD))
+ .append(Component.text(" You do not have enough mana to cast "))
+ .append(Component.text(this.name, NamedTextColor.BLUE, TextDecoration.BOLD))
+ .append(Component.text("!"))
+ .build();
+
+ player.sendMessage(manaMessage);
+ }
+ } else {
+ // Cooldown is active, so tell them how long they have to wait
+ // this is horrible, but paper has forced my hand
+ final TextComponent cooldownMessage = Component.text()
+ .append(Component.text("[!]", NamedTextColor.RED, TextDecoration.BOLD))
+ .append(Component.text(" You must wait "))
+ .append(Component.text(String.format("%.1fs", cooldown), NamedTextColor.RED, TextDecoration.BOLD))
+ .append(Component.text(" before casting "))
+ .append(Component.text(this.name, NamedTextColor.BLUE, TextDecoration.BOLD))
+ .append(Component.text(" again."))
+ .build();
+
+ player.sendMessage(cooldownMessage);
+ }
+ }
+ }
+ }
+
+ public void setOverrides(@Nullable SpellOverride overrides) {
+ if (overrides != null) {
+ if (overrides.OverrideTool != null) {
+ // Validate tool
+ try {
+ Material.valueOf(overrides.OverrideTool);
+ this.item = overrides.OverrideTool;
+ Logger.info("Overriding item material for spell " + this.name + " to " + overrides.OverrideTool);
+ } catch (IllegalArgumentException e) {
+ Logger.warning("Invalid item material provided for spell " + this.name + "!");
+ Logger.warning("Item material provided: " + overrides.OverrideTool);
+ Logger.warning("Falling back to default item material.");
+ }
+ }
+ if (overrides.OverrideManaCost != null && overrides.OverrideManaCost != 0) {
+ this.manaCost = overrides.OverrideManaCost;
+ Logger.info("Overriding mana cost for spell " + this.name + " to " + overrides.OverrideManaCost);
+ }
+ if (overrides.OverrideCooldown != null && overrides.OverrideCooldown != 0) {
+ this.cooldown = overrides.OverrideCooldown;
+ Logger.info("Overriding cooldown for spell " + this.name + " to " + overrides.OverrideCooldown);
+ }
+ if (overrides.OverrideDamage != null && overrides.OverrideDamage != 0) {
+ this.damage = overrides.OverrideDamage;
+ Logger.info("Overriding damage for spell " + this.name + " to " + overrides.OverrideDamage);
+ }
+ } else {
+ Logger.warning("setOverrides() was called for spell " + this.name + " but no overrides were provided - falling back to defaults.");
}
}
}
diff --git a/src/main/java/me/sticksdev/runicspells/structures/SpellOverride.java b/src/main/java/me/sticksdev/runicspells/structures/SpellOverride.java
new file mode 100644
index 0000000..fd091b3
--- /dev/null
+++ b/src/main/java/me/sticksdev/runicspells/structures/SpellOverride.java
@@ -0,0 +1,34 @@
+package me.sticksdev.runicspells.structures;
+import org.jetbrains.annotations.Nullable;
+
+public class SpellOverride {
+ @Nullable
+ String OverrideTool;
+
+ @Nullable
+ Integer OverrideManaCost;
+
+ @Nullable
+ Integer OverrideCooldown;
+
+ @Nullable
+ Integer OverrideDamage;
+
+ public SpellOverride(@Nullable String overrideSpellTool, @Nullable String overrideManaCost, @Nullable String overrideCooldown, @Nullable String overrideDamage) {
+ if (overrideSpellTool != null) {
+ this.OverrideTool = overrideSpellTool;
+ }
+
+ if (overrideManaCost != null) {
+ this.OverrideManaCost = Integer.parseInt(overrideManaCost);
+ }
+
+ if (overrideCooldown != null) {
+ this.OverrideCooldown = Integer.parseInt(overrideCooldown);
+ }
+
+ if (overrideDamage != null) {
+ this.OverrideDamage = Integer.parseInt(overrideDamage);
+ }
+ }
+}
diff --git a/src/main/java/me/sticksdev/runicspells/utils/Logger.java b/src/main/java/me/sticksdev/runicspells/utils/Logger.java
new file mode 100644
index 0000000..f116a78
--- /dev/null
+++ b/src/main/java/me/sticksdev/runicspells/utils/Logger.java
@@ -0,0 +1,21 @@
+package me.sticksdev.runicspells.utils;
+
+import me.sticksdev.runicspells.Runic_spells;
+import org.jetbrains.annotations.NotNull;
+
+public class Logger {
+ private static final Runic_spells plugin = Runic_spells.getInstance();
+ private static final java.util.logging.@NotNull Logger logger = plugin.getLogger();
+
+ public static void info(String message) {
+ logger.info(message);
+ }
+
+ public static void warning(String message) {
+ logger.warning(message);
+ }
+
+ public static void severe(String message) {
+ logger.severe(message);
+ }
+}
diff --git a/src/main/java/me/sticksdev/runicspells/utils/Redis.java b/src/main/java/me/sticksdev/runicspells/utils/Redis.java
new file mode 100644
index 0000000..a38696b
--- /dev/null
+++ b/src/main/java/me/sticksdev/runicspells/utils/Redis.java
@@ -0,0 +1,41 @@
+package me.sticksdev.runicspells.utils;
+
+import me.sticksdev.runicspells.Runic_spells;
+import redis.clients.jedis.CommandObject;
+import redis.clients.jedis.JedisPooled;
+import redis.clients.jedis.params.SetParams;
+
+import java.util.HashMap;
+
+public class Redis {
+ private final Runic_spells plugin = Runic_spells.getInstance();
+ private final String host = plugin.getConfigHandler().getConfig().getString("redis.host");
+ private final int port = plugin.getConfigHandler().getConfig().getInt("redis.port");
+
+ // Create a new JedisPooled instance
+ private static JedisPooled pool;
+
+ public void init() {
+ try {
+ Logger.info("Connecting to Redis...");
+ pool = new JedisPooled(host, port);
+ Logger.info("JedisPooled instance created, testing connection...");
+ pool.set("test", "test");
+ pool.del("test");
+ Logger.info("Successfully connected to Redis!");
+ } catch (Exception e) {
+ Logger.severe("Failed to connect to Redis - see stack trace below:");
+ e.printStackTrace();
+ Logger.severe("Disabling plugin due to Redis connection failure...");
+ plugin.getServer().getPluginManager().disablePlugin(plugin);
+ }
+ }
+
+ public JedisPooled getPool() {
+ return pool;
+ }
+
+ public void close() {
+ pool.close();
+ }
+}
diff --git a/src/main/java/me/sticksdev/runicspells/utils/Utils.java b/src/main/java/me/sticksdev/runicspells/utils/Utils.java
new file mode 100644
index 0000000..5e609db
--- /dev/null
+++ b/src/main/java/me/sticksdev/runicspells/utils/Utils.java
@@ -0,0 +1,17 @@
+package me.sticksdev.runicspells.utils;
+
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.util.Vector;
+
+public class Utils {
+ public static Vector getProjectileVelocity(Player player, Entity target) {
+ Vector direction;
+ if (target != null) {
+ direction = target.getLocation().toVector().subtract(player.getLocation().toVector());
+ } else {
+ direction = player.getEyeLocation().getDirection();
+ }
+ return direction.normalize().multiply(1.5);
+ }
+}
diff --git a/src/main/java/me/sticksdev/runicspells/utils/Yaml.java b/src/main/java/me/sticksdev/runicspells/utils/Yaml.java
index 274ff18..998da99 100644
--- a/src/main/java/me/sticksdev/runicspells/utils/Yaml.java
+++ b/src/main/java/me/sticksdev/runicspells/utils/Yaml.java
@@ -1,21 +1,23 @@
package me.sticksdev.runicspells.utils;
import me.sticksdev.runicspells.Runic_spells;
+import me.sticksdev.runicspells.structures.SpellOverride;
+import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
+import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
+import java.util.List;
public class Yaml {
// Basic YAML Loader and data holder for minecraft plugins
- private final Runic_spells plugin;
+ private final Runic_spells plugin = Runic_spells.getInstance();
public FileConfiguration spellsConfig;
+ public FileConfiguration configConfig;
private File spellsFilePath;
-
- public Yaml(Runic_spells plugin) {
- this.plugin = plugin;
- }
+ private File configFilePath;
/**
@@ -23,19 +25,36 @@ public class Yaml {
*/
public void init() {
spellsFilePath = new File(plugin.getDataFolder(), "spells.yml");
+ configFilePath = new File(plugin.getDataFolder(), "config.yml");
if (!spellsFilePath.exists()) {
- plugin.getLogger().info("Spells.yml not found, creating...");
+ Logger.info("Spells.yml not found, creating...");
spellsFilePath.getParentFile().mkdirs();
plugin.saveResource("spells.yml", false);
}
+ if (!configFilePath.exists()) {
+ Logger.info("Config.yml not found, creating...");
+ configFilePath.getParentFile().mkdirs();
+ plugin.saveResource("config.yml", false);
+ }
+
spellsConfig = new YamlConfiguration();
try {
spellsConfig.load(spellsFilePath);
- plugin.getLogger().info("Spells.yml loaded!");
+ Logger.info("Spells.yml loaded!");
} catch (IOException | InvalidConfigurationException e) {
- plugin.getLogger().warning("Spells.yml failed to load - see stack trace below:");
+ Logger.severe("Spells.yml failed to load - see stack trace below:");
+ e.printStackTrace();
+ }
+
+ // Load config.yml
+ configConfig = new YamlConfiguration();
+ try {
+ configConfig.load(configFilePath);
+ Logger.info("Config.yml loaded!");
+ } catch (IOException | InvalidConfigurationException e) {
+ Logger.severe("Config.yml failed to load - see stack trace below:");
e.printStackTrace();
}
}
@@ -46,19 +65,71 @@ public class Yaml {
public void destroy() {
spellsConfig = null;
spellsFilePath = null;
+ configConfig = null;
+ configFilePath = null;
}
+ /**
+ * Returns the currently loaded spells config file
+ * @return FileConfiguration
+ * @throws IllegalStateException if the config file hasn't been loaded yet
+ */
+ public FileConfiguration getSpellsConfig() {
+ if (spellsConfig == null) {
+ throw new IllegalStateException("YamlError: Config not loaded yet, have you called init()?");
+ }
+
+ return spellsConfig;
+ }
+
/**
* Returns the currently loaded config file
* @return FileConfiguration
* @throws IllegalStateException if the config file hasn't been loaded yet
*/
public FileConfiguration getConfig() {
- if (spellsConfig == null) {
+ if (configConfig == null) {
throw new IllegalStateException("YamlError: Config not loaded yet, have you called init()?");
}
- return spellsConfig;
+ return configConfig;
}
-}
+
+ @Nullable
+ public SpellOverride getSpellOverrides(String spellName) {
+ ConfigurationSection spellOverridesBlock = getSpellsConfig().getConfigurationSection("overrideSpells");
+
+ if (spellOverridesBlock == null) {
+ Logger.warning("overrideSpells is either null or commented out in spells.yml - not returning any overrides");
+ return null;
+ }
+
+ ConfigurationSection spellOverrides = spellOverridesBlock.getConfigurationSection(spellName);
+
+ if (spellOverrides == null) {
+ Logger.warning("Spell overrides for " + spellName + " is either null or commented out in spells.yml - not returning any overrides");
+ return null;
+ }
+
+ String overrideSpellTool = spellOverrides.getString("OverrideTool");
+ String overrideManaCost = spellOverrides.getString("OverrideManaCost");
+ String overrideCooldown = spellOverrides.getString("OverrideCooldown");
+ String overrideDamage = spellOverrides.getString("OverrideDamage");
+
+ return new SpellOverride(overrideSpellTool, overrideManaCost, overrideCooldown, overrideDamage);
+ }
+
+
+ public boolean getIsSpellEnabled(String spellName) {
+ // Check if it's in the enabledSpells list
+ List> enabledSpells = getSpellsConfig().getList("enabledSpells");
+
+ if (enabledSpells == null) {
+ Logger.warning("enabledSpells is either null or commented out in spells.yml - not returning any overrides");
+ return false;
+ }
+
+ return enabledSpells.contains(spellName);
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
new file mode 100644
index 0000000..67521c4
--- /dev/null
+++ b/src/main/resources/config.yml
@@ -0,0 +1,7 @@
+# Runic_Spells Developer configuration file
+# DO NOT EDIT THIS FILE IF YOU DON'T KNOW WHAT YOU ARE DOING
+
+# Redis configuration
+redis:
+ host: localhost
+ port: 6379
\ No newline at end of file
diff --git a/src/main/resources/spells.yml b/src/main/resources/spells.yml
index 20486cd..640e9ea 100644
--- a/src/main/resources/spells.yml
+++ b/src/main/resources/spells.yml
@@ -6,6 +6,9 @@
# If you want to disable a spell, just remove it from this list
enabledSpells:
- "Fireball"
+ - "Earth"
+ - "Water"
+ - "Lightning"
# Override blocks
# You can override any spell as long as it's enabled in the list above