diff --git a/CHANGELOG.md b/CHANGELOG.md index 0617029..e98fad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,12 @@ +## 2.0.0 +- Renamed plugin to "BetterTridents" +- Added new mechanics and config options: + - Bedrock impaling: Impaling enchantment behaves like in Bedrock (it damages ALL mobs touching water, not only ocean mobs). Default: Enabled + - Bedrock drop chance: Raises the drop chance for tridents to the bedrock value (25% for every drowned yielding a trident + 4% per looting level) Default: Enabled + - Return to offhand: Tridents thrown from the offhand will also return to the offhand when picking them up. Default: Enabled + - Disable loyalty portals: Prohibit tridents with Loyalty to travel through portals to avoid losing them. Default: Enabled +- Added automatic config updater +- Added reload command (/bettertridents, permission bettertridents.reload) + ## 1.1.0 - Added UpdateChecker and bStats \ No newline at end of file diff --git a/pom.xml b/pom.xml index ed2acb3..40626da 100644 --- a/pom.xml +++ b/pom.xml @@ -4,13 +4,13 @@ 4.0.0 de.jeff_media - NoTridentVoid - NoTridentVoid - 1.1.0 + BetterTridents + BetterTridents + 2.0.0 ${project.name} - ${project.groupId}.notridentvoid.Main + ${project.groupId}.bettertridents.Main UTF-8 1.8 @@ -45,7 +45,7 @@ de.jeff_media.updatechecker - de.jeff_media.notridentvoid.updatechecker + de.jeff_media.bettertridents.updatechecker org.bstats diff --git a/src/main/java/de/jeff_media/notridentvoid/Main.java b/src/main/java/de/jeff_media/bettertridents/Main.java similarity index 54% rename from src/main/java/de/jeff_media/notridentvoid/Main.java rename to src/main/java/de/jeff_media/bettertridents/Main.java index b2dc576..df64c5b 100644 --- a/src/main/java/de/jeff_media/notridentvoid/Main.java +++ b/src/main/java/de/jeff_media/bettertridents/Main.java @@ -1,7 +1,9 @@ -package de.jeff_media.notridentvoid; +package de.jeff_media.bettertridents; -import de.jeff_media.notridentvoid.config.Config; -import de.jeff_media.notridentvoid.listeners.ProjectileListener; +import de.jeff_media.bettertridents.commands.ReloadCommand; +import de.jeff_media.bettertridents.config.Config; +import de.jeff_media.bettertridents.config.ConfigUpdater; +import de.jeff_media.bettertridents.listeners.*; import de.jeff_media.updatechecker.UpdateChecker; import de.jeff_media.updatechecker.UserAgentBuilder; import org.bstats.bukkit.Metrics; @@ -11,6 +13,7 @@ import org.bukkit.NamespacedKey; import org.bukkit.entity.Trident; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; import java.util.ArrayList; import java.util.UUID; @@ -20,22 +23,41 @@ public class Main extends JavaPlugin { private final ArrayList tridents = new ArrayList<>(); public static final Material SAFETY_MATERIAL = Material.BARRIER; public static NamespacedKey LOYALTY_TAG; + public static NamespacedKey IMPALING_TAG; + public static NamespacedKey OFFHAND_TAG; public static Main getInstance() { return instance; } + private boolean debug = false; + + public void debug(String text) { + if(debug) getLogger().warning("[DEBUG] " + text); + } @Override public void onEnable() { instance = this; LOYALTY_TAG = new NamespacedKey(this, "loyalty"); + IMPALING_TAG = new NamespacedKey(this, "impaling"); + OFFHAND_TAG = new NamespacedKey(this, "offhand"); reload(); - Bukkit.getPluginManager().registerEvents(new ProjectileListener(), this); + Bukkit.getPluginManager().registerEvents(new VoidListener(), this); + Bukkit.getPluginManager().registerEvents(new DropListener(), this); + Bukkit.getPluginManager().registerEvents(new ImpalingListener(), this); + Bukkit.getPluginManager().registerEvents(new OffhandListener(), this); + Bukkit.getPluginManager().registerEvents(new TridentThrowListener(), this); + getCommand("bettertridents").setExecutor(new ReloadCommand()); @SuppressWarnings("unused") Metrics metrics = new Metrics(this, 11460); } public void reload() { + if(!new File(getDataFolder(), "config.yml").exists()) { + saveDefaultConfig(); + } + reloadConfig(); new Config(); + ConfigUpdater.updateConfig(); UpdateChecker.init(this, "https://api.jeff-media.de/notridentvoid/latest-version.txt") .setDonationLink("https://paypal.me/mfnalex") .setDownloadLink(92656) @@ -47,17 +69,21 @@ public class Main extends JavaPlugin { } else if(getConfig().getString(Config.CHECK_FOR_UPDATES).equalsIgnoreCase("on-startup")) { UpdateChecker.getInstance().checkNow(); } + debug = getConfig().getBoolean(Config.DEBUG); + if(debug) { + getLogger().warning("Debug mode enabled - this may affect performance."); + } } - public void register(Trident trident) { + public void setLoyal(Trident trident) { tridents.add(trident.getUniqueId()); } - public boolean isRegistered(Trident trident) { + public boolean isLoyal(Trident trident) { return tridents.contains(trident.getUniqueId()); } - public void unregister(Trident trident) { + public void removeLoyal(Trident trident) { tridents.remove(trident.getUniqueId()); } } diff --git a/src/main/java/de/jeff_media/bettertridents/commands/ReloadCommand.java b/src/main/java/de/jeff_media/bettertridents/commands/ReloadCommand.java new file mode 100644 index 0000000..68054cc --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/commands/ReloadCommand.java @@ -0,0 +1,27 @@ +package de.jeff_media.bettertridents.commands; + +import de.jeff_media.bettertridents.Main; +import de.jeff_media.bettertridents.config.Permissions; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +public class ReloadCommand implements CommandExecutor { + + private final Main main = Main.getInstance(); + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + + if(!sender.hasPermission(Permissions.RELOAD)) { + sender.sendMessage(command.getPermissionMessage()); + return true; + } + + main.reload(); + sender.sendMessage(ChatColor.DARK_GREEN + main.getDescription().getName() + " v" + main.getDescription().getVersion() + ChatColor.GREEN + " has been reloaded."); + return true; + } +} diff --git a/src/main/java/de/jeff_media/bettertridents/config/Config.java b/src/main/java/de/jeff_media/bettertridents/config/Config.java new file mode 100644 index 0000000..cd325c0 --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/config/Config.java @@ -0,0 +1,35 @@ +package de.jeff_media.bettertridents.config; + +import de.jeff_media.bettertridents.Main; + +public class Config { + + private final Main main = Main.getInstance(); + + public static final String BEDROCK_IMPALING = "bedrock-impaling"; + public static final String CONFIG_PLUGIN_VERSION = "plugin-version"; + public static final String VOID_SAVING = "void-saving"; + public static final String CONFIG_VERSION = "config-version"; + public static final String CHECK_FOR_UPDATES = "check-for-updates"; + public static final String UPDATE_CHECK_INTERVAL = "update-check-interval"; + public static final String DROP_BEDROCK_CHANCE = "bedrock-drop-chance"; + public static final String RETURN_TO_OFFHAND = "return-to-offhand"; + public static final String DISABLE_LOYALTY_PORTALS = "disable-loyalty-portals"; + public static final String DEBUG = "debug"; + + public Config() { + addDefault(VOID_SAVING, true); + addDefault(DROP_BEDROCK_CHANCE, true); + addDefault(CHECK_FOR_UPDATES, "true"); + addDefault(UPDATE_CHECK_INTERVAL, 4); + addDefault(BEDROCK_IMPALING, true); + addDefault(RETURN_TO_OFFHAND, true); + addDefault(DISABLE_LOYALTY_PORTALS, true); + addDefault(DEBUG, false); + } + + private void addDefault(String node, Object value) { + main.getConfig().addDefault(node, value); + } + +} diff --git a/src/main/java/de/jeff_media/bettertridents/config/ConfigUpdater.java b/src/main/java/de/jeff_media/bettertridents/config/ConfigUpdater.java new file mode 100644 index 0000000..9a25e1b --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/config/ConfigUpdater.java @@ -0,0 +1,208 @@ +package de.jeff_media.bettertridents.config; + +import de.jeff_media.bettertridents.Main; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; + +/** + * Updates the config file. When a new config file is shipped with AngelChest, it will save the new + * file and replace all default values with the values that were set in the old config file. + */ +public final class ConfigUpdater { + + // Lines STARTING WITH these names will be treated as String lists + private static final String[] LINES_CONTAINING_STRING_LISTS = {}; + // Lines STARTING WITH these names will never get the old value applied + private static final String[] LINES_IGNORED = {"config-version:", "plugin-version:"}; + // Lines STARTING WITH these names will get no quotes although they would match one of the lists below + private static final String[] CONFLICTING_NODES_NEEDING_NO_QUOTES = {}; + // Lines STARTING WITH these names will get their values wrapped in double quotes + private static final String[] NODES_NEEDING_DOUBLE_QUOTES = {}; + // Lines STARTING WITH these names will get their values wrapped in single quotes + private static final String[] NODES_NEEDING_SINGLE_QUOTES = {}; + + private static void backupCurrentConfig(final Main main) { + final File oldFile = new File(getFilePath(main, "config.yml")); + final File newFile = new File(getFilePath(main, "config-backup-" + main.getConfig().getString(Config.CONFIG_PLUGIN_VERSION) + ".yml")); + if (newFile.exists()) newFile.delete(); + if (oldFile.getAbsoluteFile().renameTo(newFile.getAbsoluteFile())) { + main.debug("Could not rename " + oldFile.getAbsolutePath() + " to " + newFile.getAbsolutePath()); + } + } + + /** + * For debugging the config updater only + */ + private static void debug(final Logger logger, final String message) { + if (false) { + logger.warning(message); + } + } + + private static String getFilePath(final Main main, final String fileName) { + return main.getDataFolder() + File.separator + fileName; + } + + private static List getNewConfigAsArrayList(final Main main) { + final List lines; + try { + lines = Files.readAllLines(Paths.get(getFilePath(main, "config.yml")), StandardCharsets.UTF_8); + return lines; + } catch (final IOException ioException) { + ioException.printStackTrace(); + } + return null; + } + + /** + * Returns the config version of the currently installed AngelChest default config + * + * @return default config version + */ + private static long getNewConfigVersion() { + final InputStream in = Main.getInstance().getClass().getResourceAsStream("/config-version.txt"); + final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + try { + return Long.parseLong(reader.readLine()); + } catch (final IOException ioException) { + ioException.printStackTrace(); + return 0; + } + + } + + /** + * Returns a String representing the correct quotes to use for this key's value + * + * @param line line/key to get the quotes for + * @return double quote, single quote or empty string, according to the key name + */ + private static String getQuotes(final String line) { + for(final String test : CONFLICTING_NODES_NEEDING_NO_QUOTES) { + if(line.startsWith(test)) { + return ""; + } + } + for (final String test : NODES_NEEDING_DOUBLE_QUOTES) { + if (line.startsWith(test)) { + return "\""; + } + } + for (final String test : NODES_NEEDING_SINGLE_QUOTES) { + if (line.startsWith(test)) { + return "'"; + } + } + return ""; + } + + private static boolean lineContainsIgnoredNode(final String line) { + for (final String test : LINES_IGNORED) { + if (line.startsWith(test)) { + return true; + } + } + return false; + } + + private static boolean lineIsStringList(final String line) { + for (final String test : LINES_CONTAINING_STRING_LISTS) { + if (line.startsWith(test)) { + return true; + } + } + return false; + } + + private static void saveArrayListToConfig(final Main main, final List lines) { + try { + final BufferedWriter fw = Files.newBufferedWriter(new File(getFilePath(main, "config.yml")).toPath(), StandardCharsets.UTF_8); + for (final String line : lines) { + fw.write(line + System.lineSeparator()); + } + fw.close(); + } catch (final IOException ioException) { + ioException.printStackTrace(); + } + } + + /** + * Attempts to update the config + */ + public static void updateConfig() { + final Main main = Main.getInstance(); + final Logger logger = main.getLogger(); + debug(logger, "Newest config version = " + getNewConfigVersion()); + debug(logger, "Current config version = " + main.getConfig().getLong(Config.CONFIG_VERSION)); + if (main.getConfig().getLong(Config.CONFIG_VERSION) >= getNewConfigVersion()) { + debug(logger, "The config currently used has an equal or newer version than the one shipped with this release."); + return; + } + + logger.info("==========================================="); + logger.info("You are using an outdated config file."); + logger.info("Your config file will now be updated to the"); + logger.info("newest version. You changes will be kept."); + logger.info("==========================================="); + + backupCurrentConfig(main); + main.saveDefaultConfig(); + + final Set oldConfigNodes = main.getConfig().getKeys(false); + final ArrayList newConfig = new ArrayList<>(); + + // Iterate through ALL lines from the new default config + for (final String defaultLine : getNewConfigAsArrayList(main)) { + + String updatedLine = defaultLine; + + /*if (Daddy.allows(Features.GENERIC)) { + if (updatedLine.startsWith("# PREMIUM FEATURE: ONLY AVAILABLE IN AngelChestPlus!")) { + updatedLine = null; + } + } else*/ + if (defaultLine.startsWith("-") || defaultLine.startsWith(" -") || defaultLine.startsWith(" -")) { + debug(logger, "Not including default String list entry: " + defaultLine); + updatedLine = null; + } else if (lineContainsIgnoredNode(defaultLine)) { + debug(logger, "Not updating this line: " + defaultLine); + } else if (lineIsStringList(defaultLine)) { + updatedLine = null; + newConfig.add(defaultLine); + final String node = defaultLine.split(":")[0]; + for (final String entry : main.getConfig().getStringList(node)) { + newConfig.add("- " + entry); + } + } else { + for (final String node : oldConfigNodes) { + // Iterate through all keys from the old config file. + if (defaultLine.startsWith(node + ":")) { + // This key from the old file matches this line from the new file! Updating... + final String quotes = getQuotes(node); + String value = main.getConfig().get(node).toString(); + + // The hologram text needs special escaping for the newline symbols + if (node.equals("hologram-text")) { + value = value.replaceAll("\n", "\\\\n"); + } + + updatedLine = node + ": " + quotes + value + quotes; + } + } + } + + if (updatedLine != null) { + newConfig.add(updatedLine); + } + } + + saveArrayListToConfig(main, newConfig); + } +} diff --git a/src/main/java/de/jeff_media/bettertridents/config/Permissions.java b/src/main/java/de/jeff_media/bettertridents/config/Permissions.java new file mode 100644 index 0000000..990b82e --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/config/Permissions.java @@ -0,0 +1,6 @@ +package de.jeff_media.bettertridents.config; + +public class Permissions { + public static final String SAVE_VOID = "bettertridents.savevoid"; + public static final String RELOAD = "bettertridents.reload"; +} diff --git a/src/main/java/de/jeff_media/bettertridents/listeners/DropListener.java b/src/main/java/de/jeff_media/bettertridents/listeners/DropListener.java new file mode 100644 index 0000000..d56c031 --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/listeners/DropListener.java @@ -0,0 +1,60 @@ +package de.jeff_media.bettertridents.listeners; + +import de.jeff_media.bettertridents.Main; +import de.jeff_media.bettertridents.config.Config; +import de.jeff_media.bettertridents.utils.EnchantmentUtils; +import org.bukkit.GameRule; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Drowned; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Random; + +public class DropListener implements Listener { + + private final Main main = Main.getInstance(); + final Random random = new Random(); + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onKillDrowned(EntityDeathEvent event) { + if (!main.getConfig().getBoolean(Config.DROP_BEDROCK_CHANCE)) return; + if (!(event.getEntity().getLastDamageCause() instanceof EntityDamageByEntityEvent)) return; + EntityDamageByEntityEvent damageEvent = (EntityDamageByEntityEvent) event.getEntity().getLastDamageCause(); + if (damageEvent.getDamager().getType() != EntityType.PLAYER) return; + Player lastDamager = (Player) damageEvent.getDamager(); + int fortune = EnchantmentUtils.getLevelFromTrident(lastDamager, Enchantment.LOOT_BONUS_MOBS); + if (!(event.getEntity() instanceof Drowned)) return; + + Drowned drowned = (Drowned) event.getEntity(); + if (!drowned.getWorld().getGameRuleValue(GameRule.DO_MOB_LOOT)) return; + if (drowned.getEquipment().getItemInMainHand().getType() != Material.TRIDENT) return; + + for (ItemStack drop : event.getDrops()) { + if (drop.getType() == Material.TRIDENT) return; + } + + main.debug("Using bedrock drop chance..."); + + int chance = random.nextInt(100); + main.debug("Drop chance: " + chance); + if (chance > 25 + (4 * fortune)) return; + ItemStack trident = new ItemStack(Material.TRIDENT); + Damageable meta = (Damageable) trident.getItemMeta(); + meta.setDamage(random.nextInt(248) + 1); + trident.setItemMeta((ItemMeta) meta); + event.getDrops().add(trident); + + } + + +} diff --git a/src/main/java/de/jeff_media/bettertridents/listeners/ImpalingListener.java b/src/main/java/de/jeff_media/bettertridents/listeners/ImpalingListener.java new file mode 100644 index 0000000..250ec9e --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/listeners/ImpalingListener.java @@ -0,0 +1,111 @@ +package de.jeff_media.bettertridents.listeners; + +import de.jeff_media.bettertridents.Main; +import de.jeff_media.bettertridents.config.Config; +import de.jeff_media.bettertridents.utils.EnchantmentUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.*; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; + +import java.util.Random; + +public class ImpalingListener implements Listener { + + private final Main main = Main.getInstance(); + + private final Random random = new Random(); + + private static boolean isAquatic(EntityType type) { + switch (type) { + case DOLPHIN: + case GUARDIAN: + case ELDER_GUARDIAN: + case SQUID: + case TURTLE: + case COD: + case SALMON: + case PUFFERFISH: + case TROPICAL_FISH: + return true; + default: + return false; + } + } + + private static boolean isInRain(Entity entity) { + World world = entity.getWorld(); + if (!world.hasStorm()) return false; + Location min = entity.getBoundingBox().getMin().toLocation(world); + Location max = entity.getBoundingBox().getMax().toLocation(world); + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + Block highest = world.getHighestBlockAt(x, z); + if (highest == null || highest.getType().isAir()) return true; + if (highest.getY() < entity.getLocation().getY()) return true; + } + } + return false; + } + + private static boolean isInWater(Entity entity) { + World world = entity.getWorld(); + Location min = entity.getBoundingBox().getMin().toLocation(entity.getWorld()); + Location max = entity.getBoundingBox().getMax().toLocation(entity.getWorld()); + for (int x = min.getBlockX(); x <= max.getBlockX(); x++) { + for (int y = min.getBlockY(); y <= max.getBlockY(); y++) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) { + if (world.getBlockAt(x, y, z).getType() == Material.WATER) { + return true; + } + } + } + } + return false; + } + + private void displayEnchantedHit(Entity entity) { + for (int i = 0; i < 16 * 3; i++) { + double d = (this.random.nextFloat() * 2.0F - 1.0F); + double e = (this.random.nextFloat() * 2.0F - 1.0F); + double f = (this.random.nextFloat() * 2.0F - 1.0F); + if (Math.sqrt(d) + Math.sqrt(e) + Math.sqrt(f) <= 1.0D) { + Location loc = entity.getLocation().clone(); + loc.add((entity.getWidth() * (d / 4.0D)), + (entity.getHeight() * (0.5D + e / 4.0D)), + (entity.getWidth() * (f / 4.0D))); + loc.getWorld().spawnParticle(Particle.CRIT_MAGIC, loc, 0, d, e + 0.2D, f); + } + } + } + + + + @EventHandler(ignoreCancelled = true) + private void onHitByImpaling(EntityDamageByEntityEvent event) { + if (!main.getConfig().getBoolean(Config.BEDROCK_IMPALING)) return; + + Entity entity = event.getEntity(); + int impaling = 0; + if (event.getDamager().getType() == EntityType.TRIDENT) { + impaling = EnchantmentUtils.getImpaling((Trident) event.getDamager()); + } else if (event.getDamager().getType() == EntityType.PLAYER) { + impaling = ((Player) event.getDamager()).getInventory().getItemInMainHand().getEnchantmentLevel(Enchantment.IMPALING); + } + + if (impaling > 0) { + if (!isAquatic(entity.getType()) && (isInRain(entity) || isInWater(entity))) { + event.setDamage(event.getDamage() + (2.5 * impaling)); + main.debug("Adjusting Impaling damage: adding " + 2.5*impaling + " damage"); + displayEnchantedHit(entity); + } + } + } + +} diff --git a/src/main/java/de/jeff_media/bettertridents/listeners/OffhandListener.java b/src/main/java/de/jeff_media/bettertridents/listeners/OffhandListener.java new file mode 100644 index 0000000..4761858 --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/listeners/OffhandListener.java @@ -0,0 +1,39 @@ +package de.jeff_media.bettertridents.listeners; + +import de.jeff_media.bettertridents.Main; +import de.jeff_media.bettertridents.config.Config; +import de.jeff_media.bettertridents.tasks.MoveToOffhand; +import de.jeff_media.bettertridents.utils.EnchantmentUtils; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.entity.Trident; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerPickupArrowEvent; +import org.bukkit.inventory.ItemStack; + +public class OffhandListener implements Listener { + + private final Main main = Main.getInstance(); + + @EventHandler(ignoreCancelled = true) + private void onPickupTrident(PlayerPickupArrowEvent event) { + main.debug("onPickupTrident"); + if (!main.getConfig().getBoolean(Config.RETURN_TO_OFFHAND)) return; + if (!(event.getArrow() instanceof Trident)) return; + Trident trident = (Trident) event.getArrow(); + if(!EnchantmentUtils.isOffhandThrown(trident)) { + main.debug("This trident wasn't thrown from the offhand."); + return; + } + Player player = event.getPlayer(); + if (player.getInventory().getItemInOffHand().getType() != Material.AIR) return; + + ItemStack tridentItem = event.getItem().getItemStack().clone(); + + main.debug("Starting offhand task..."); + new MoveToOffhand(player, tridentItem).runTask(main); + } +} + + diff --git a/src/main/java/de/jeff_media/bettertridents/listeners/PortalListener.java b/src/main/java/de/jeff_media/bettertridents/listeners/PortalListener.java new file mode 100644 index 0000000..0f2286f --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/listeners/PortalListener.java @@ -0,0 +1,28 @@ +package de.jeff_media.bettertridents.listeners; + +import de.jeff_media.bettertridents.Main; +import de.jeff_media.bettertridents.config.Config; +import de.jeff_media.bettertridents.utils.EnchantmentUtils; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Trident; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPortalEvent; + +public class PortalListener implements Listener { + + private final Main main = Main.getInstance(); + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onTridentEnterPortal(EntityPortalEvent event) { + if(!main.getConfig().getBoolean(Config.DISABLE_LOYALTY_PORTALS)) return; + if(event.getEntityType() != EntityType.TRIDENT) return; + Trident trident = (Trident) event.getEntity(); + if(EnchantmentUtils.getLoyalty(trident)>0) { + main.debug("Prevented loyalty trident from travelling through portqal"); + event.setCancelled(true); + } + } + +} diff --git a/src/main/java/de/jeff_media/bettertridents/listeners/TridentThrowListener.java b/src/main/java/de/jeff_media/bettertridents/listeners/TridentThrowListener.java new file mode 100644 index 0000000..0ae2e58 --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/listeners/TridentThrowListener.java @@ -0,0 +1,49 @@ +package de.jeff_media.bettertridents.listeners; + +import de.jeff_media.bettertridents.Main; +import de.jeff_media.bettertridents.utils.EnchantmentUtils; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Trident; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.persistence.PersistentDataType; + +public class TridentThrowListener implements Listener { + + private final Main main = Main.getInstance(); + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onTridentThrow(ProjectileLaunchEvent event) { + if (event.getEntityType() != EntityType.TRIDENT) return; + main.debug("Trident throw Listener: ProjectileLaunchEvent"); + + Trident trident = (Trident) event.getEntity(); + if (!(trident.getShooter() instanceof Player)) { + main.debug("This trident wasn't thrown by a player."); + return; + } + Player player = (Player) trident.getShooter(); + + // Impaling + int impaling = EnchantmentUtils.getLevelFromTrident(player, Enchantment.IMPALING); + main.debug("Applying impaling level " + impaling); + EnchantmentUtils.registerImpaling((Trident) event.getEntity(), impaling); + + // Loyalty + int loyalty = EnchantmentUtils.getLevelFromTrident(player, Enchantment.LOYALTY); + main.debug("Applying loyalty level " + loyalty); + EnchantmentUtils.registerLoyalty((Trident) event.getEntity(), loyalty); + + // Offhand + if (player.getInventory().getItemInMainHand().getType() == Material.TRIDENT) return; + if (player.getInventory().getItemInOffHand().getType() != Material.TRIDENT) return; + trident.getPersistentDataContainer().set(Main.OFFHAND_TAG, PersistentDataType.BYTE, (byte) 1); + main.debug("This trident was thrown by the offhand."); + } + +} diff --git a/src/main/java/de/jeff_media/bettertridents/listeners/VoidListener.java b/src/main/java/de/jeff_media/bettertridents/listeners/VoidListener.java new file mode 100644 index 0000000..b07730d --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/listeners/VoidListener.java @@ -0,0 +1,64 @@ +package de.jeff_media.bettertridents.listeners; + +import de.jeff_media.bettertridents.Main; +import de.jeff_media.bettertridents.config.Config; +import de.jeff_media.bettertridents.tasks.WatchTrident; +import de.jeff_media.bettertridents.utils.EnchantmentUtils; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.entity.Trident; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.inventory.ItemStack; + +public class VoidListener implements Listener { + + private final Main main = Main.getInstance(); + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onThrowTrident(ProjectileLaunchEvent event) { + main.debug("VoidListener"); + if (event.getEntityType() != EntityType.TRIDENT) { + main.debug("Not a trident"); + return; + } + Trident trident = (Trident) event.getEntity(); + //if (main.isLoyal(trident)) return; + if (!(trident.getShooter() instanceof Player)) { + main.debug("Not shot by player"); + return; + } + Player player = (Player) trident.getShooter(); + ItemStack tridentItem = null; + if (player.getInventory().getItemInOffHand() != null) { + if (player.getInventory().getItemInOffHand().getType() == Material.TRIDENT) { + tridentItem = player.getInventory().getItemInOffHand(); + } + } + if (player.getInventory().getItemInMainHand() != null) { + if (player.getInventory().getItemInMainHand().getType() == Material.TRIDENT) { + tridentItem = player.getInventory().getItemInMainHand(); + } + } + if (tridentItem == null) { + main.debug("tridentItem not found"); + return; + } + if (!EnchantmentUtils.hasLoyalty(tridentItem)) { + main.debug("No loyalty"); + return; + } + if(!main.getConfig().getBoolean(Config.VOID_SAVING)) { + main.debug("Void Saving disabled"); + return; + } + main.setLoyal(trident); + main.debug("New task: WatchTrident"); + new WatchTrident(trident).runTaskTimer(main,1,1); + Bukkit.getScheduler().runTaskLater(main,() ->main.removeLoyal(trident),1200); + } +} diff --git a/src/main/java/de/jeff_media/bettertridents/tasks/MoveToOffhand.java b/src/main/java/de/jeff_media/bettertridents/tasks/MoveToOffhand.java new file mode 100644 index 0000000..106d1d9 --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/tasks/MoveToOffhand.java @@ -0,0 +1,27 @@ +package de.jeff_media.bettertridents.tasks; + +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; + +public class MoveToOffhand extends BukkitRunnable { + private final Player player; + private final ItemStack tridentItem; + + public MoveToOffhand(Player player, ItemStack tridentItem) { + this.player = player; + this.tridentItem = tridentItem; + } + + @Override + public void run() { + for (ItemStack item : player.getInventory()) { + if (item != null && item.equals(tridentItem)) { + player.getInventory().remove(item); + player.getInventory().setItemInOffHand(item.clone()); + break; + } + } + player.updateInventory(); + } +} diff --git a/src/main/java/de/jeff_media/notridentvoid/tasks/RemoveBarrier.java b/src/main/java/de/jeff_media/bettertridents/tasks/RemoveBarrier.java similarity index 91% rename from src/main/java/de/jeff_media/notridentvoid/tasks/RemoveBarrier.java rename to src/main/java/de/jeff_media/bettertridents/tasks/RemoveBarrier.java index 1d17e85..c29146e 100644 --- a/src/main/java/de/jeff_media/notridentvoid/tasks/RemoveBarrier.java +++ b/src/main/java/de/jeff_media/bettertridents/tasks/RemoveBarrier.java @@ -1,6 +1,6 @@ -package de.jeff_media.notridentvoid.tasks; +package de.jeff_media.bettertridents.tasks; -import de.jeff_media.notridentvoid.Main; +import de.jeff_media.bettertridents.Main; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Block; diff --git a/src/main/java/de/jeff_media/notridentvoid/tasks/WatchTrident.java b/src/main/java/de/jeff_media/bettertridents/tasks/WatchTrident.java similarity index 89% rename from src/main/java/de/jeff_media/notridentvoid/tasks/WatchTrident.java rename to src/main/java/de/jeff_media/bettertridents/tasks/WatchTrident.java index 244c518..5ddd7b6 100644 --- a/src/main/java/de/jeff_media/notridentvoid/tasks/WatchTrident.java +++ b/src/main/java/de/jeff_media/bettertridents/tasks/WatchTrident.java @@ -1,6 +1,6 @@ -package de.jeff_media.notridentvoid.tasks; +package de.jeff_media.bettertridents.tasks; -import de.jeff_media.notridentvoid.Main; +import de.jeff_media.bettertridents.Main; import org.bukkit.Location; import org.bukkit.entity.Trident; import org.bukkit.scheduler.BukkitRunnable; @@ -37,7 +37,7 @@ public class WatchTrident extends BukkitRunnable { } nextLocation.getBlock().setType(Main.SAFETY_MATERIAL); - + Main.getInstance().debug("New Task: RemoveBarrier"); new RemoveBarrier(trident, nextLocation.getBlock()).runTaskTimer(Main.getInstance(), 1, 1); cancel(); } diff --git a/src/main/java/de/jeff_media/bettertridents/utils/EnchantmentUtils.java b/src/main/java/de/jeff_media/bettertridents/utils/EnchantmentUtils.java new file mode 100644 index 0000000..e977cef --- /dev/null +++ b/src/main/java/de/jeff_media/bettertridents/utils/EnchantmentUtils.java @@ -0,0 +1,56 @@ +package de.jeff_media.bettertridents.utils; + +import de.jeff_media.bettertridents.Main; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.entity.Trident; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; + +public class EnchantmentUtils { + + public static int getLevelFromTrident(Player player, Enchantment enchantment) { + ItemStack item = player.getInventory().getItemInMainHand(); + if(item.getType()!= Material.TRIDENT) { + item = player.getInventory().getItemInOffHand(); + } + if(item.getType()!=Material.TRIDENT) return 0; + if (!item.hasItemMeta()) return 0; + ItemMeta meta = item.getItemMeta(); + if (meta.hasEnchant(enchantment)) { + return meta.getEnchantLevel(enchantment); + } + return 0; + } + + public static int getImpaling(Trident trident) { + return trident.getPersistentDataContainer().getOrDefault(Main.IMPALING_TAG, PersistentDataType.INTEGER, 0); + } + + public static void registerImpaling(Trident trident, int level) { + if(level == 0) return; + trident.getPersistentDataContainer().set(Main.IMPALING_TAG, PersistentDataType.INTEGER, level); + } + + public static int getLoyalty(Trident trident) { + return trident.getPersistentDataContainer().getOrDefault(Main.LOYALTY_TAG, PersistentDataType.INTEGER, 0); + } + + public static void registerLoyalty(Trident trident, int level) { + if(level == 0) return; + trident.getPersistentDataContainer().set(Main.LOYALTY_TAG, PersistentDataType.INTEGER, level); + } + + public static boolean isOffhandThrown(Trident trident) { + return trident.getPersistentDataContainer().has(Main.OFFHAND_TAG, PersistentDataType.BYTE); + } + + public static boolean hasLoyalty(ItemStack item) { + if (!item.hasItemMeta()) return false; + ItemMeta meta = item.getItemMeta(); + return meta.hasEnchant(Enchantment.LOYALTY); + } + +} diff --git a/src/main/java/de/jeff_media/notridentvoid/config/Config.java b/src/main/java/de/jeff_media/notridentvoid/config/Config.java deleted file mode 100644 index 9764ab0..0000000 --- a/src/main/java/de/jeff_media/notridentvoid/config/Config.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.jeff_media.notridentvoid.config; - -import de.jeff_media.notridentvoid.Main; - -public class Config { - - private final Main main = Main.getInstance(); - - public static final String VOID_SAVING = "void-saving"; - public static final String CHECK_FOR_UPDATES = "check-for-updates"; - public static final String UPDATE_CHECK_INTERVAL = "update-check-interval"; - - public Config() { - addDefault(VOID_SAVING, true); - } - - private void addDefault(String node, Object value) { - main.getConfig().set(node, value); - } - -} diff --git a/src/main/java/de/jeff_media/notridentvoid/config/Permissions.java b/src/main/java/de/jeff_media/notridentvoid/config/Permissions.java deleted file mode 100644 index 9dd0abd..0000000 --- a/src/main/java/de/jeff_media/notridentvoid/config/Permissions.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.jeff_media.notridentvoid.config; - -public class Permissions { - public static final String NOTRIDENTVOID_USE = "notridentvoid.use"; -} diff --git a/src/main/java/de/jeff_media/notridentvoid/listeners/ProjectileListener.java b/src/main/java/de/jeff_media/notridentvoid/listeners/ProjectileListener.java deleted file mode 100644 index 16921f0..0000000 --- a/src/main/java/de/jeff_media/notridentvoid/listeners/ProjectileListener.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.jeff_media.notridentvoid.listeners; - -import de.jeff_media.notridentvoid.Main; -import de.jeff_media.notridentvoid.config.Config; -import de.jeff_media.notridentvoid.tasks.WatchTrident; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import org.bukkit.entity.Trident; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.ProjectileLaunchEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -public class ProjectileListener implements Listener { - - private final Main main = Main.getInstance(); - - private boolean hasLoyalty(ItemStack item) { - if(!item.hasItemMeta()) return false; - ItemMeta meta = item.getItemMeta(); - return meta.hasEnchant(Enchantment.LOYALTY); - } - - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void onShoot(ProjectileLaunchEvent event) { - if(event.getEntityType() != EntityType.TRIDENT) return; - Trident trident = (Trident) event.getEntity(); - if(main.isRegistered(trident)) return; - //if(!hasLoyalty(trident)) return; - if(!(trident.getShooter() instanceof Player)) return; - Player player = (Player) trident.getShooter(); - ItemStack tridentItem = null; - if(player.getInventory().getItemInOffHand() != null) { - if(player.getInventory().getItemInOffHand().getType() == Material.TRIDENT) { - tridentItem = player.getInventory().getItemInOffHand(); - } - } - if(player.getInventory().getItemInMainHand() != null) { - if(player.getInventory().getItemInMainHand().getType() == Material.TRIDENT) { - tridentItem = player.getInventory().getItemInMainHand(); - } - } - if(tridentItem == null) return; - if(!hasLoyalty(tridentItem)) return; - if(!main.getConfig().getBoolean(Config.VOID_SAVING)) return; - main.register(trident); - new WatchTrident(trident).runTaskTimer(main,1,1); - Bukkit.getScheduler().runTaskLater(main,() ->main.unregister(trident),20); - } -} diff --git a/src/main/resources/config-version.txt b/src/main/resources/config-version.txt new file mode 100644 index 0000000..eaa3551 --- /dev/null +++ b/src/main/resources/config-version.txt @@ -0,0 +1 @@ +${config.version} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ae1c936..af7dcc4 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -8,7 +8,7 @@ # Permissions # ############### -# notridentvoid.use +# bettertridents.savevoid # Allows to use the "void-saving" feature # Default: true @@ -17,6 +17,22 @@ # This is currently the only function of this plugin :P void-saving: true +# When enabled, the Impaling enchantment behaves like in Bedrock edition: it deals extra damage to mobs +# touching water or rain. +# In 1.17+, this is no longer needed ( https://minecraft.fandom.com/wiki/Java_Edition_Combat_Test_4 ) +bedrock-impaling: true + +# When enabled, Drowned drop tridents as often as in Bedrock edition. +# Default Java Edition drop chance: 8.5% (up to 11.5% with Looting 3) for all Drowned holding a trident +# Bedrock Edition drop chance: 25% (up to 37% with Looting 3) for all Drowned holding a trident +bedrock-drop-chance: true + +# When enabled, tridents thrown from the offhand will go back to the offhand slot when picked up +return-to-offhand: true + +# When enabled, tridents enchanted with loyalty will be prevented from travelling through portals +disable-loyalty-portals: true + # Should we check for updates? # When enabled, a message is printed in the console if a new version has # been found, and OPs will be notified when they join the server. @@ -28,6 +44,9 @@ check-for-updates: true # When check-for-updates is true, AngelChest will check every X hours update-check-interval: 4 +# Debug mode - you probably don't want this +debug: false + # NEVER CHANGE THE VALUES BELOW! plugin-version: ${project.version} config-version: ${config.version} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index ed4c62e..e9a9159 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -10,7 +10,19 @@ load: STARTUP awareness: - !@UTF8 commands: + bettertridents: + description: Reloads the config file + permission: bettertridents.reload + usage: / permissions: - notridentvoid.use: + bettertridents.*: + description: Allows to use all BetterTridents features + children: + - bettertridents.savevoid + default: op + bettertridents.savevoid: description: Prevents tridents enchanted with loyalty to get lost in the void - default: true \ No newline at end of file + default: true + bettertridents.reload: + description: Allows to reload the config file using /bettertridents + default: op \ No newline at end of file