commit 0ed8e725e7d5cda7e08f3175376d0f6399be2b5d Author: Big Pappa Date: Sat Aug 8 17:39:22 2026 -0500 1.0.0 First Version. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..81e199a --- /dev/null +++ b/pom.xml @@ -0,0 +1,108 @@ + + + + 4.0.0 + + com.rsscripting + rs-disenchanter + RS-DISenchanter + 1.0.0 + + jar + + + 21 + UTF-8 + + + + clean package + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + ${java.version} + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.3 + + + package + + shade + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + rename-final-jar + package + + run + + + + + + + + + + + + + + + + + + + + + src/main/resources + true + + + + + + + + papermc-repo + https://repo.papermc.io/repository/maven-public/ + + + + + + io.papermc.paper + paper-api + 1.21.11-R0.1-SNAPSHOT + provided + + + + \ No newline at end of file diff --git a/rs-disenchanter.iml b/rs-disenchanter.iml new file mode 100644 index 0000000..3cf00db --- /dev/null +++ b/rs-disenchanter.iml @@ -0,0 +1,13 @@ + + + + + + + PAPER + + 1 + + + + \ No newline at end of file diff --git a/src/main/java/com/rsscripting/rsdisenchanter/RSDISenchanter.java b/src/main/java/com/rsscripting/rsdisenchanter/RSDISenchanter.java new file mode 100644 index 0000000..8138bc4 --- /dev/null +++ b/src/main/java/com/rsscripting/rsdisenchanter/RSDISenchanter.java @@ -0,0 +1,37 @@ +package com.rsscripting.rsdisenchanter; + +import com.rsscripting.rsdisenchanter.command.DisenchantCommand; +import com.rsscripting.rsdisenchanter.language.LanguageManager; +import com.rsscripting.rsdisenchanter.listener.DisenchanterListener; +import org.bukkit.plugin.java.JavaPlugin; + +public final class RSDISenchanter extends JavaPlugin { + + private LanguageManager languageManager; + + @Override + public void onEnable() { + saveDefaultConfig(); + + languageManager = new LanguageManager(this); + languageManager.load(); + + getCommand("disenchant").setExecutor(new DisenchantCommand(this)); + + getServer().getPluginManager().registerEvents( + new DisenchanterListener(this), + this + ); + + getLogger().info("RS-DISenchanter enabled."); + } + + @Override + public void onDisable() { + getLogger().info("RS-DISenchanter disabled."); + } + + public LanguageManager getLanguageManager() { + return languageManager; + } +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/rsdisenchanter/command/DisenchantCommand.java b/src/main/java/com/rsscripting/rsdisenchanter/command/DisenchantCommand.java new file mode 100644 index 0000000..7317ff9 --- /dev/null +++ b/src/main/java/com/rsscripting/rsdisenchanter/command/DisenchantCommand.java @@ -0,0 +1,43 @@ +package com.rsscripting.rsdisenchanter.command; + +import com.rsscripting.rsdisenchanter.RSDISenchanter; +import com.rsscripting.rsdisenchanter.gui.DisenchanterGUI; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class DisenchantCommand implements CommandExecutor { + + private final RSDISenchanter plugin; + + public DisenchantCommand(RSDISenchanter plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand( + CommandSender sender, + Command command, + String label, + String[] args + ) { + if (!(sender instanceof Player player)) { + sender.sendMessage( + plugin.getLanguageManager().get("messages.general.player-only") + ); + return true; + } + + if (!player.hasPermission("rsdisenchanter.use")) { + player.sendMessage( + plugin.getLanguageManager().get("messages.general.no-permission") + ); + return true; + } + + new DisenchanterGUI(plugin).open(player); + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/rsdisenchanter/enchantment/EnchantmentManager.java b/src/main/java/com/rsscripting/rsdisenchanter/enchantment/EnchantmentManager.java new file mode 100644 index 0000000..9daef47 --- /dev/null +++ b/src/main/java/com/rsscripting/rsdisenchanter/enchantment/EnchantmentManager.java @@ -0,0 +1,101 @@ +package com.rsscripting.rsdisenchanter.enchantment; + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class EnchantmentManager { + + /** + * Gets all enchantments on an item. + */ + public Map getEnchantments(ItemStack item) { + Map enchantments = new LinkedHashMap<>(); + + if (item == null) { + return enchantments; + } + + if (item.getItemMeta() instanceof EnchantmentStorageMeta bookMeta) { + enchantments.putAll(bookMeta.getStoredEnchants()); + } else { + enchantments.putAll(item.getEnchantments()); + } + + return enchantments; + } + + /** + * Gets only cursed enchantments on an item. + */ + public Map getCurses(ItemStack item) { + Map curses = new LinkedHashMap<>(); + + for (Map.Entry entry : + getEnchantments(item).entrySet()) { + + if (entry.getKey().isCursed()) { + curses.put(entry.getKey(), entry.getValue()); + } + } + + return curses; + } + + /** + * Gets all non-cursed enchantments on an item. + */ + public Map getNormalEnchantments(ItemStack item) { + Map enchantments = new LinkedHashMap<>(); + + for (Map.Entry entry : + getEnchantments(item).entrySet()) { + + if (!entry.getKey().isCursed()) { + enchantments.put(entry.getKey(), entry.getValue()); + } + } + + return enchantments; + } + + /** + * Creates a standard Minecraft enchanted book. + */ + public ItemStack createEnchantedBook( + Enchantment enchantment, + int level + ) { + ItemStack book = new ItemStack(Material.ENCHANTED_BOOK); + + EnchantmentStorageMeta meta = + (EnchantmentStorageMeta) book.getItemMeta(); + + if (meta != null) { + meta.addStoredEnchant(enchantment, level, true); + book.setItemMeta(meta); + } + + return book; + } + + /** + * Determines which enchantments should currently be displayed. + * + * Curses always take priority. Normal enchantments are displayed + * only after all curses have been removed. + */ + public Map getDisplayEnchantments(ItemStack item) { + Map curses = getCurses(item); + + if (!curses.isEmpty()) { + return curses; + } + + return getNormalEnchantments(item); + } +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/rsdisenchanter/gui/DisenchanterGUI.java b/src/main/java/com/rsscripting/rsdisenchanter/gui/DisenchanterGUI.java new file mode 100644 index 0000000..f806f2b --- /dev/null +++ b/src/main/java/com/rsscripting/rsdisenchanter/gui/DisenchanterGUI.java @@ -0,0 +1,131 @@ +package com.rsscripting.rsdisenchanter.gui; + +import com.rsscripting.rsdisenchanter.RSDISenchanter; +import com.rsscripting.rsdisenchanter.enchantment.EnchantmentManager; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import java.util.HashMap; + +import java.util.HashMap; +import java.util.Map; + +public class DisenchanterGUI implements InventoryHolder { + + private final RSDISenchanter plugin; + private final Inventory inventory; + private final Map removedCurseBooks = new HashMap<>(); + + public DisenchanterGUI(RSDISenchanter plugin) { + this.plugin = plugin; + + String title = plugin.getLanguageManager() + .get("gui.titles.disenchanter"); + + inventory = Bukkit.createInventory(this, 27, title); + + setupInventory(); + } + + private void setupInventory() { + ItemStack glass = createGlassPane(); + + // Left 3x3 border + inventory.setItem(0, glass); + inventory.setItem(1, glass); + inventory.setItem(2, glass); + + inventory.setItem(9, glass); + inventory.setItem(11, glass); + + inventory.setItem(18, glass); + inventory.setItem(19, glass); + inventory.setItem(20, glass); + } + + private ItemStack createGlassPane() { + ItemStack item = new ItemStack(Material.GRAY_STAINED_GLASS_PANE); + ItemMeta meta = item.getItemMeta(); + + if (meta != null) { + meta.setDisplayName( + plugin.getLanguageManager() + .get("items.gui.glass-pane.name") + ); + + item.setItemMeta(meta); + } + + return item; + } + + public void open(Player player) { + player.openInventory(inventory); + } + + public RSDISenchanter getPlugin() { + return plugin; + } + + @Override + public Inventory getInventory() { + + return inventory; + } + + public void displayEnchantments( + Map enchantments, + EnchantmentManager enchantmentManager + ) { + clearBookSlots(); + + int[] bookSlots = { + 3, 4, 5, 6, 7, 8, + 12, 13, 14, 15, 16, 17, + 21, 22, 23, 24, 25, 26 + }; + + int slotIndex = 0; + + for (Map.Entry entry : enchantments.entrySet()) { + + if (slotIndex >= bookSlots.length) { + break; + } + + ItemStack book = enchantmentManager.createEnchantedBook( + entry.getKey(), + entry.getValue() + ); + + inventory.setItem(bookSlots[slotIndex], book); + slotIndex++; + } + } + + public void clearBookSlots() { + int[] bookSlots = { + 3, 4, 5, 6, 7, 8, + 12, 13, 14, 15, 16, 17, + 21, 22, 23, 24, 25, 26 + }; + + for (int slot : bookSlots) { + inventory.setItem(slot, null); + } + } + + public void recordRemovedCurse(Enchantment enchantment) { + removedCurseBooks.merge(enchantment, 1, Integer::sum); + } + + public Map getRemovedCurseBooks() { + return new HashMap<>(removedCurseBooks); + } + +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/rsdisenchanter/language/LanguageManager.java b/src/main/java/com/rsscripting/rsdisenchanter/language/LanguageManager.java new file mode 100644 index 0000000..da66047 --- /dev/null +++ b/src/main/java/com/rsscripting/rsdisenchanter/language/LanguageManager.java @@ -0,0 +1,72 @@ +package com.rsscripting.rsdisenchanter.language; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.util.Map; + +public class LanguageManager { + + private final JavaPlugin plugin; + private FileConfiguration language; + + public LanguageManager(JavaPlugin plugin) { + this.plugin = plugin; + } + + public void load() { + String languageName = plugin.getConfig().getString("language", "en_us"); + + File languageFolder = new File(plugin.getDataFolder(), "language"); + + if (!languageFolder.exists() && !languageFolder.mkdirs()) { + plugin.getLogger().warning("Could not create language folder."); + } + + File languageFile = new File(languageFolder, languageName + ".yml"); + + if (!languageFile.exists()) { + plugin.saveResource("language/" + languageName + ".yml", false); + } + + language = YamlConfiguration.loadConfiguration(languageFile); + } + + public String get(String path) { + if (language == null) { + return ""; + } + + String message = language.getString(path, ""); + String prefix = language.getString("prefix", ""); + + message = message.replace("{prefix}", prefix); + + return color(message); + } + + public String get(String path, Map variables) { + String message = get(path); + + for (Map.Entry entry : variables.entrySet()) { + message = message.replace( + "{" + entry.getKey() + "}", + entry.getValue() + ); + } + + return message; + } + + public String getEnchantmentName(Enchantment enchantment) { + return get("enchantments." + enchantment.getKey().getKey()); + } + + private String color(String message) { + return ChatColor.translateAlternateColorCodes('&', message); + } +} \ No newline at end of file diff --git a/src/main/java/com/rsscripting/rsdisenchanter/listener/DisenchanterListener.java b/src/main/java/com/rsscripting/rsdisenchanter/listener/DisenchanterListener.java new file mode 100644 index 0000000..bd2b33f --- /dev/null +++ b/src/main/java/com/rsscripting/rsdisenchanter/listener/DisenchanterListener.java @@ -0,0 +1,386 @@ +package com.rsscripting.rsdisenchanter.listener; + +import com.rsscripting.rsdisenchanter.RSDISenchanter; +import com.rsscripting.rsdisenchanter.enchantment.EnchantmentManager; +import com.rsscripting.rsdisenchanter.gui.DisenchanterGUI; +import org.bukkit.Bukkit; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; + +import java.util.Map; + +public class DisenchanterListener implements Listener { + + private final RSDISenchanter plugin; + private final EnchantmentManager enchantmentManager; + + public DisenchanterListener(RSDISenchanter plugin) { + this.plugin = plugin; + this.enchantmentManager = new EnchantmentManager(); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + if (!(event.getInventory().getHolder() instanceof DisenchanterGUI gui)) { + return; + } + + if (event.isShiftClick()) { + event.setCancelled(true); + return; + } + + int rawSlot = event.getRawSlot(); + + // Player inventory. + if (rawSlot >= event.getView().getTopInventory().getSize()) { + return; + } + + // Center input slot. + if (rawSlot == 10) { + if (event.getWhoClicked() instanceof Player player) { + Bukkit.getScheduler().runTask( + plugin, + () -> validateInput(player, gui) + ); + } + + return; + } + + if (isBookSlot(rawSlot)) { + event.setCancelled(true); + + if (!(event.getWhoClicked() instanceof Player player)) { + return; + } + + ItemStack book = event.getCurrentItem(); + + if (book == null || book.getType().isAir()) { + return; + } + + handleBookClick(player, gui, book, rawSlot); + return; + } + + // All other GUI slots are protected. + event.setCancelled(true); + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) { + if (!(event.getInventory().getHolder() instanceof DisenchanterGUI)) { + return; + } + + if (!(event.getPlayer() instanceof Player player)) { + return; + } + + ItemStack item = event.getInventory().getItem(10); + + if (item == null || item.getType().isAir()) { + return; + } + + event.getInventory().setItem(10, null); + + Map leftovers = player.getInventory().addItem(item); + + for (ItemStack leftover : leftovers.values()) { + player.getWorld().dropItemNaturally( + player.getLocation(), + leftover + ); + } + } + + @EventHandler + public void onInventoryDrag(InventoryDragEvent event) { + if (!(event.getInventory().getHolder() instanceof DisenchanterGUI)) { + return; + } + + for (int slot : event.getRawSlots()) { + if (slot != 10) { + event.setCancelled(true); + return; + } + } + } + + private void validateInput(Player player, DisenchanterGUI gui) { + + ItemStack item = gui.getInventory().getItem(10); + + if (item == null || item.getType().isAir()) { + gui.clearBookSlots(); + return; + } + + if (enchantmentManager.getEnchantments(item).isEmpty()) { + + gui.clearBookSlots(); + + gui.getInventory().setItem(10, null); + + player.getInventory().addItem(item); + + player.sendMessage( + plugin.getLanguageManager() + .get("messages.general.no-enchantments") + ); + + return; + } + + gui.displayEnchantments( + enchantmentManager.getDisplayEnchantments(item), + enchantmentManager + ); + } + + private boolean isBookSlot(int slot) { + return slot == 3 || slot == 4 || slot == 5 + || slot == 6 || slot == 7 || slot == 8 + || slot == 12 || slot == 13 || slot == 14 + || slot == 15 || slot == 16 || slot == 17 + || slot == 21 || slot == 22 || slot == 23 + || slot == 24 || slot == 25 || slot == 26; + } + + private void handleBookClick( + Player player, + DisenchanterGUI gui, + ItemStack book, + int bookSlot + ) { + if (!(book.getItemMeta() instanceof EnchantmentStorageMeta meta)) { + return; + } + + Map storedEnchantments = + meta.getStoredEnchants(); + + if (storedEnchantments.size() != 1) { + return; + } + + Map.Entry entry = + storedEnchantments.entrySet().iterator().next(); + + Enchantment enchantment = entry.getKey(); + int level = entry.getValue(); + + ItemStack item = gui.getInventory().getItem(10); + + if (item == null || item.getType().isAir()) { + return; + } + + if (!enchantmentManager.getEnchantments(item) + .containsKey(enchantment)) { + return; + } + + if (enchantment.isCursed()) { + handleCurseClick(player, gui, item, enchantment, level, bookSlot); + return; + } + + ItemStack resultBook = enchantmentManager.createEnchantedBook( + enchantment, + level + ); + + Map leftovers = + player.getInventory().addItem(resultBook); + + if (!leftovers.isEmpty()) { + player.sendMessage( + plugin.getLanguageManager() + .get("messages.inventory.full") + ); + return; + } + + item.removeEnchantment(enchantment); + + gui.getInventory().setItem(10, item); + +// Remove the book that was clicked. + gui.getInventory().setItem(bookSlot, null); + + gui.displayEnchantments( + enchantmentManager.getDisplayEnchantments(item), + enchantmentManager + ); + + } + + private void handleCurseClick( + Player player, + DisenchanterGUI gui, + ItemStack item, + Enchantment enchantment, + int level, + int bookSlot + ) { + ItemStack resultBook = enchantmentManager.createEnchantedBook( + enchantment, + level + ); + + Map leftovers = + player.getInventory().addItem(resultBook); + + if (!leftovers.isEmpty()) { + player.sendMessage( + plugin.getLanguageManager() + .get("messages.inventory.full") + ); + return; + } + + double destructionChance = + plugin.getConfig().getDouble( + "curse-destruction-chance", + 5.0 + ); + + double roll = Math.random() * 100.0; + + if (roll < destructionChance) { + destroyFailedCurseSession(player, gui, item, enchantment); + return; + } + + // Successful curse removal. + item.removeEnchantment(enchantment); + + gui.getInventory().setItem(10, item); + gui.getInventory().setItem(bookSlot, null); + + gui.recordRemovedCurse(enchantment); + + String message = plugin.getLanguageManager() + .get("messages.curse.removed") + .replace( + "{enchantment}", + plugin.getLanguageManager() + .getEnchantmentName(enchantment) + ) + .replace("{item}", getItemName(item)); + + player.sendMessage(message); + + gui.displayEnchantments( + enchantmentManager.getDisplayEnchantments(item), + enchantmentManager + ); + } + + private void destroyFailedCurseSession( + Player player, + DisenchanterGUI gui, + ItemStack item, + Enchantment failedCurse + ) { + removeSessionCurseBooks( + player, + gui.getRemovedCurseBooks() + ); + + gui.getInventory().setItem(10, null); + gui.clearBookSlots(); + + String message = plugin.getLanguageManager() + .get("messages.curse.failed") + .replace( + "{enchantment}", + plugin.getLanguageManager() + .getEnchantmentName(failedCurse) + ) + .replace( + "{item}", + getItemName(item) + ); + + player.sendMessage(message); + + player.closeInventory(); + } + + private void removeSessionCurseBooks( + Player player, + Map booksToRemove + ) { + for (Map.Entry entry : + booksToRemove.entrySet()) { + + Enchantment enchantment = entry.getKey(); + int amountToRemove = entry.getValue(); + + for (ItemStack inventoryItem : + player.getInventory().getContents()) { + + if (amountToRemove <= 0) { + break; + } + + if (inventoryItem == null) { + continue; + } + + if (!(inventoryItem.getItemMeta() + instanceof EnchantmentStorageMeta meta)) { + continue; + } + + Map stored = + meta.getStoredEnchants(); + + if (stored.size() != 1 + || !stored.containsKey(enchantment)) { + continue; + } + + int removeAmount = Math.min( + amountToRemove, + inventoryItem.getAmount() + ); + + inventoryItem.setAmount( + inventoryItem.getAmount() - removeAmount + ); + + amountToRemove -= removeAmount; + } + } + } + + private String getItemName(ItemStack item) { + if (item.hasItemMeta() + && item.getItemMeta().hasDisplayName()) { + return item.getItemMeta().getDisplayName(); + } + + String name = item.getType().name() + .toLowerCase() + .replace('_', ' '); + + return Character.toUpperCase(name.charAt(0)) + + name.substring(1); + } + +} \ 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..c16aec1 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,15 @@ +# ========================================== +# RS-DISenchanter Configuration +# ========================================== + +# Language file to use. +# The file must exist in the language folder. +language: en_us + +# Chance, in percent, that an item is destroyed +# when attempting to remove a curse. +# +# 0 = Never destroyed +# 5 = 5% chance +# 100 = Always destroyed +curse-destruction-chance: 5 \ No newline at end of file diff --git a/src/main/resources/language/en_us.yml b/src/main/resources/language/en_us.yml new file mode 100644 index 0000000..c78c0da --- /dev/null +++ b/src/main/resources/language/en_us.yml @@ -0,0 +1,86 @@ +# ========================================== +# RS-DISenchanter - English Language File +# ========================================== + +gui: + + titles: + disenchanter: "&8Disenchanter" + + +messages: + + general: + no-permission: "{prefix}&cYou do not have permission to use the Disenchanter." + player-only: "{prefix}&cThis command can only be used by a player." + invalid-item: "{prefix}&cThat item cannot be used in the Disenchanter." + no-enchantments: "{prefix}&cThat item has no enchantments to remove." + + + curse: + removed: "{prefix}&aYou successfully removed {enchantment} from the {item}." + failed: "{prefix}&cBad luck! You failed to remove {enchantment} from the {item} and the {item} was destroyed." + + disenchant: + completed: "{prefix}&aThe enchantment was successfully removed." + + inventory: + full: "{prefix}&cYour inventory is full." + + +items: + + gui: + glass-pane: + name: " " + + +errors: + + language: + file-not-found: "{prefix}&cThe language file could not be found." + invalid-language: "{prefix}&cThe configured language file is invalid." + +enchantments: + aqua_affinity: "Aqua Affinity" + bane_of_arthropods: "Bane of Arthropods" + binding_curse: "Curse of Binding" + blast_protection: "Blast Protection" + breach: "Breach" + channeling: "Channeling" + density: "Density" + depth_strider: "Depth Strider" + efficiency: "Efficiency" + feather_falling: "Feather Falling" + fire_aspect: "Fire Aspect" + fortune: "Fortune" + frost_walker: "Frost Walker" + impaling: "Impaling" + knockback: "Knockback" + looting: "Looting" + loyalty: "Loyalty" + luck_of_the_sea: "Luck of the Sea" + lure: "Lure" + mending: "Mending" + multishot: "Multishot" + piercing: "Piercing" + power: "Power" + projectile_protection: "Projectile Protection" + protection: "Protection" + punch: "Punch" + quick_charge: "Quick Charge" + respiration: "Respiration" + riptide: "Riptide" + sharpness: "Sharpness" + silk_touch: "Silk Touch" + smite: "Smite" + soul_speed: "Soul Speed" + sweeping_edge: "Sweeping Edge" + swift_sneak: "Swift Sneak" + thorns: "Thorns" + unbreaking: "Unbreaking" + vanishing_curse: "Curse of Vanishing" + wind_burst: "Wind Burst" + + +prefix: "&8[&dDisenchanter&8] " \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..a6f89d3 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,17 @@ +name: RS-DISenchanter +version: ${project.version} +main: com.rsscripting.rsdisenchanter.RSDISenchanter +api-version: '1.21' +author: Big Pappa, Atlas +description: Enchantment/Cursed Item Removal Tool + +commands: + disenchant: + description: Opens the Disenchanter + usage: /disenchant + permission: rsdisenchanter.use + +permissions: + rsdisenchanter.use: + description: Allows the player to use the Disenchanter + default: true \ No newline at end of file