Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package pro.cloudnode.smp.enchantbookplus;

import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

public final class ConfigEnchantmentEntry {
/**
* Name of the enchantment.
*/
private final @NotNull String name;

/**
* Maximum level of the enchantment.
*/
private final @Nullable Integer maxLevel;

/**
* Max level relative
*/
private final boolean maxLevelRelative;

/**
* Cost of the enchantment.
*/
private final int cost;

/**
* Multiply cost by level.
*/
private final boolean multiplyCostByLevel;

/**
* Name of the enchantment.
*/
public @NotNull String getName() {
return name;
}

/**
* Maximum level of the enchantment.
*/
public @NotNull Optional<Integer> getMaxLevel() {
if (Optional.ofNullable(maxLevel).isEmpty()) return Optional.empty();
if (maxLevelRelative) return Optional.of(getEnchantment().getMaxLevel() + maxLevel);
return Optional.of(maxLevel);
}

/**
* Cost of the enchantment.
*/
public int getCost() {
return cost;
}

/**
* Multiply cost by level.
*/
public boolean getMultiplyCostByLevel() {
return multiplyCostByLevel;
}

/**
* Get enchantment
*/
public Enchantment getEnchantment() {
return Enchantment.getByKey(NamespacedKey.minecraft(name));
}

/**
* Is enchantment
*
* @param enchantment The enchantment
*/
public boolean isEnchantment(final @NotNull Enchantment enchantment) {
return name.equalsIgnoreCase(enchantment.getKey().getKey());
}

/**
* @param name Name of the enchantment.
* @param maxLevel Maximum level of the enchantment.
* @param maxLevelRelative Max level relative
* @param cost Cost of the enchantment.
* @param multiplyCostByLevel Multiply cost by level.
*/
public ConfigEnchantmentEntry(final @NotNull String name, final @Nullable Integer maxLevel, final boolean maxLevelRelative, final int cost, final boolean multiplyCostByLevel) {
this.name = name;
this.maxLevel = maxLevel;
this.maxLevelRelative = maxLevelRelative;
this.cost = cost;
this.multiplyCostByLevel = multiplyCostByLevel;
}

/**
* From config object
*
* @param configValue Config object
*/
public static ConfigEnchantmentEntry configValue(final @NotNull HashMap<@NotNull String, @NotNull Object> configValue) {
final @NotNull String name = (String) Objects.requireNonNull(configValue.get("name"));
final @Nullable Integer maxLevel;
final boolean maxLevelRelative;
if (configValue.containsKey("max-level")) {
if (configValue.get("max-level") instanceof final @NotNull String string) {
if (string.startsWith("+")) {
maxLevel = Integer.parseInt(string.substring(1));
maxLevelRelative = true;
}
else {
maxLevel = Integer.parseInt(string);
maxLevelRelative = false;
}
}
else {
maxLevel = (Integer) configValue.get("max-level");
maxLevelRelative = false;
}
}
else {
maxLevel = null;
maxLevelRelative = false;
}
final boolean multiplyCostByLevel;
final int cost;
if (configValue.containsKey("cost")) {
if (configValue.get("cost") instanceof final @NotNull String costString) {
if (costString.startsWith("*")) {
multiplyCostByLevel = true;
cost = Integer.parseInt(costString.substring(1));
}
else {
multiplyCostByLevel = false;
cost = Integer.parseInt(costString);
}
}
else {
multiplyCostByLevel = false;
cost = (Integer) configValue.get("cost");
}
}
else {
multiplyCostByLevel = false;
cost = 0;
}
return new ConfigEnchantmentEntry(name, maxLevel, maxLevelRelative, cost, multiplyCostByLevel);
}


/**
* From config object array
*
* @param configValue Config object array
*/
public static @NotNull List<@NotNull ConfigEnchantmentEntry> configArray(final @NotNull ArrayList<@NotNull HashMap<@NotNull String, @NotNull Object>> configValue) {
return configValue.stream().map(ConfigEnchantmentEntry::configValue).collect(Collectors.toList());
}

/**
* Check if valid config object
*
* @param configValue Config object
*/
private static boolean isValidConfigValue(final @Nullable Object configValue) {
if (configValue == null) return false;
if (!(configValue instanceof final @NotNull ArrayList<?> arrayList)) return false;
for (final @NotNull Object object : arrayList) {
if (!(object instanceof final @NotNull HashMap<?, ?> hashMap)) return false;
if (!hashMap.containsKey("name")) return false;
if (!(hashMap.get("name") instanceof String)) return false;
if (hashMap.containsKey("max-level") && !(hashMap.get("max-level") instanceof String) && !(hashMap.get("max-level") instanceof Integer))
return false;
if (hashMap.containsKey("cost") && !(hashMap.get("cost") instanceof String) && !(hashMap.get("cost") instanceof Integer))
return false;
}
return true;
}

/**
* From config
*
* @param configValue Config object
*/
public static @NotNull List<@NotNull ConfigEnchantmentEntry> config(final @Nullable Object configValue) throws IllegalArgumentException {
if (!isValidConfigValue(configValue)) throw new IllegalArgumentException("Invalid config value");
return configArray((ArrayList<HashMap<String, Object>>) configValue);
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,71 @@
package pro.cloudnode.smp.enchantbookplus;

import org.bukkit.enchantments.Enchantment;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import pro.cloudnode.smp.enchantbookplus.event.PrepareAnvil;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;

public final class EnchantBookPlus extends JavaPlugin {
/**
* Get plugin instance.
*/
public static EnchantBookPlus getInstance() {
return getPlugin(EnchantBookPlus.class);
}

/**
* Register event listeners.
*/
private void registerEvents() {
getServer().getPluginManager().registerEvents(new PrepareAnvil(), this);
}

/**
* Config enchantments cache
*/
private @NotNull List<@NotNull ConfigEnchantmentEntry> configEnchantments = new ArrayList<>();

/**
* Config enchantments cache
*/
public @NotNull List<@NotNull ConfigEnchantmentEntry> getConfigEnchantments() {
if (configEnchantments.size() == 0) reload();
return configEnchantments;
}

/**
* Get enchantment from cache
*
* @param enchantment The Minecraft enchantment
*/
public @NotNull Optional<@NotNull ConfigEnchantmentEntry> getConfigEnchantment(final @NotNull Enchantment enchantment) {
return getConfigEnchantments().stream().filter(c -> c.isEnchantment(enchantment)).findFirst();
}

/**
* Reload config
*/
public void reload() {
reloadConfig();
try {
configEnchantments = ConfigEnchantmentEntry.config(getConfig().get("enchantments"));
}
catch (final @NotNull Exception exception) {
getLogger().log(Level.SEVERE, "Failed to load config", exception);
getServer().getPluginManager().disablePlugin(this);
}
}

@Override
public void onEnable() {
registerEvents();
saveDefaultConfig();
reload();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.jetbrains.annotations.NotNull;
import pro.cloudnode.smp.enchantbookplus.ConfigEnchantmentEntry;
import pro.cloudnode.smp.enchantbookplus.EnchantBookPlus;

import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

public class PrepareAnvil implements Listener {
public final class PrepareAnvil implements Listener {
@EventHandler
public void onPrepareAnvil(final @NotNull PrepareAnvilEvent event) {
final @NotNull Optional<@NotNull ItemStack> result = Optional.ofNullable(event.getResult());
Expand All @@ -26,27 +31,41 @@ public void onPrepareAnvil(final @NotNull PrepareAnvilEvent event) {
final @NotNull Map<@NotNull Enchantment, @NotNull Integer> upgradeEnchants =
upgrade.getType() == Material.ENCHANTED_BOOK && upgrade.getItemMeta() instanceof final @NotNull EnchantmentStorageMeta upgradeMeta ?
upgradeMeta.getStoredEnchants() : upgrade.getEnchantments();
if (itemEnchants.isEmpty() || upgradeEnchants.isEmpty()) return;
if (upgradeEnchants.isEmpty()) return;
final @NotNull HashMap<@NotNull Enchantment, @NotNull Integer> upgrades = new HashMap<>();
int cost = 0;
for (final @NotNull Map.Entry<@NotNull Enchantment, @NotNull Integer> entry : upgradeEnchants.entrySet()) {
final @NotNull Enchantment enchantment = entry.getKey();
if (enchantment.getMaxLevel() == 1) continue;
final @NotNull Optional<@NotNull ConfigEnchantmentEntry> configEnchantment = EnchantBookPlus.getInstance().getConfigEnchantment(enchantment);
if (configEnchantment.isEmpty()) continue;
final int upgradeLevel = entry.getValue();
if (upgradeLevel <= enchantment.getMaxLevel()) continue;
final int finalLevel;
if (itemEnchants.containsKey(enchantment)) {
final int itemLevel = itemEnchants.get(enchantment);
if (itemLevel > upgradeLevel) upgrades.put(enchantment, itemLevel);
else if (itemLevel == upgradeLevel) upgrades.put(enchantment, upgradeLevel + 1);
else upgrades.put(enchantment, upgradeLevel);
if (itemLevel > upgradeLevel) finalLevel = itemLevel;
else if (itemLevel == upgradeLevel) finalLevel = upgradeLevel + 1;
else finalLevel = upgradeLevel;
}
else upgrades.put(enchantment, upgradeLevel);
else finalLevel = upgradeLevel;
if (finalLevel < enchantment.getMaxLevel()) continue;
if (configEnchantment.get().getMaxLevel(enchantment).isPresent() && finalLevel > configEnchantment.get().getMaxLevel(enchantment).get()) continue;
if (finalLevel > upgradeLevel) cost += configEnchantment.get().getMultiplyCostByLevel() ? configEnchantment.get().getCost() * (finalLevel - enchantment.getMaxLevel()) : configEnchantment.get().getCost();
upgrades.put(enchantment, finalLevel);
}
if (upgrades.isEmpty()) return;
final int vanillaCost = inventory.getRepairCost();
inventory.setRepairCost(vanillaCost + cost);
for (final @NotNull Map.Entry<@NotNull Enchantment, @NotNull Integer> entry : upgrades.entrySet()) {
if (result.get().getItemMeta() instanceof final @NotNull EnchantmentStorageMeta resultMeta) {
if (!resultMeta.getStoredEnchants().containsKey(entry.getKey())) continue;
resultMeta.addStoredEnchant(entry.getKey(), entry.getValue(), true);
result.get().setItemMeta(resultMeta);
}
else result.get().addUnsafeEnchantment(entry.getKey(), entry.getValue());
else {
if (!result.get().getEnchantments().containsKey(entry.getKey())) continue;
result.get().addUnsafeEnchantment(entry.getKey(), entry.getValue());
}
}
}
}
21 changes: 21 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# List of enchantments for which to enable upgrading above the vanilla max levels using an anvil.
enchantments:
# Enchantment name (same as in the /enchant command).
# Set this to "ALL" to enable upgrading for all other enchantments not listed here.
# NOTE: Enchantments that on Vanilla have a max level of 1 (e.g. Silk Touch, Mending, Curse of Vanishing, Infinity) etc. are always ignored by this plugin.
- name: efficiency
# Maximum level to which the enchantment can be upgraded.
# For relative levels above the vanilla max level, use e.g. "+1". E.g. +1 for Protection will allow upgrading to Protection V. For Efficiency, +1 will allow upgrading to Efficiency VI.
# If you use "+" make sure to set the value in quotes, e.g. "+1" instead of +1
# To allow upgrading to any level, do not set this value. Note: enchantment levels above 255 are not supported by Minecraft.
max-level: 10

# The XP cost (in levels) added to the vanilla cost
# Use "*" prefix to multiply by the enchantment level, e.g. "*10" will cost XP levels per enchantment level above vanilla (e.g. 20XP for Efficiency VII)
# If you use "*" make sure to set the value in quotes, e.g. "*10" instead of *10
# You can also just provide a flat value for all levels, e.g. "10" will cost 10 XP levels regardless of the enchantment level
cost: "*150"

- name: ALL
max-level: "+1"
cost: 100