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