Skip to content

Commit dff2db1

Browse files
authored
Merge pull request #1 from cloudnode-pro/config
Add config with per-enchant options for max level and cost
2 parents ec31ee0 + b1d8e72 commit dff2db1

File tree

4 files changed

+296
-9
lines changed

4 files changed

+296
-9
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package pro.cloudnode.smp.enchantbookplus;
2+
3+
import org.bukkit.NamespacedKey;
4+
import org.bukkit.enchantments.Enchantment;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
import java.util.ArrayList;
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Objects;
12+
import java.util.Optional;
13+
import java.util.stream.Collectors;
14+
15+
public final class ConfigEnchantmentEntry {
16+
/**
17+
* Name of the enchantment.
18+
*/
19+
private final @NotNull String name;
20+
21+
/**
22+
* Maximum level of the enchantment.
23+
*/
24+
private final @Nullable Integer maxLevel;
25+
26+
/**
27+
* Max level relative
28+
*/
29+
private final boolean maxLevelRelative;
30+
31+
/**
32+
* Cost of the enchantment.
33+
*/
34+
private final int cost;
35+
36+
/**
37+
* Multiply cost by level.
38+
*/
39+
private final boolean multiplyCostByLevel;
40+
41+
/**
42+
* Name of the enchantment.
43+
*/
44+
public @NotNull String getName() {
45+
return name;
46+
}
47+
48+
/**
49+
* Maximum level of the enchantment.
50+
*/
51+
public @NotNull Optional<Integer> getMaxLevel() {
52+
if (Optional.ofNullable(maxLevel).isEmpty()) return Optional.empty();
53+
if (maxLevelRelative) return Optional.of(getEnchantment().getMaxLevel() + maxLevel);
54+
return Optional.of(maxLevel);
55+
}
56+
57+
/**
58+
* Cost of the enchantment.
59+
*/
60+
public int getCost() {
61+
return cost;
62+
}
63+
64+
/**
65+
* Multiply cost by level.
66+
*/
67+
public boolean getMultiplyCostByLevel() {
68+
return multiplyCostByLevel;
69+
}
70+
71+
/**
72+
* Get enchantment
73+
*/
74+
public Enchantment getEnchantment() {
75+
return Enchantment.getByKey(NamespacedKey.minecraft(name));
76+
}
77+
78+
/**
79+
* Is enchantment
80+
*
81+
* @param enchantment The enchantment
82+
*/
83+
public boolean isEnchantment(final @NotNull Enchantment enchantment) {
84+
return name.equalsIgnoreCase(enchantment.getKey().getKey());
85+
}
86+
87+
/**
88+
* @param name Name of the enchantment.
89+
* @param maxLevel Maximum level of the enchantment.
90+
* @param maxLevelRelative Max level relative
91+
* @param cost Cost of the enchantment.
92+
* @param multiplyCostByLevel Multiply cost by level.
93+
*/
94+
public ConfigEnchantmentEntry(final @NotNull String name, final @Nullable Integer maxLevel, final boolean maxLevelRelative, final int cost, final boolean multiplyCostByLevel) {
95+
this.name = name;
96+
this.maxLevel = maxLevel;
97+
this.maxLevelRelative = maxLevelRelative;
98+
this.cost = cost;
99+
this.multiplyCostByLevel = multiplyCostByLevel;
100+
}
101+
102+
/**
103+
* From config object
104+
*
105+
* @param configValue Config object
106+
*/
107+
public static ConfigEnchantmentEntry configValue(final @NotNull HashMap<@NotNull String, @NotNull Object> configValue) {
108+
final @NotNull String name = (String) Objects.requireNonNull(configValue.get("name"));
109+
final @Nullable Integer maxLevel;
110+
final boolean maxLevelRelative;
111+
if (configValue.containsKey("max-level")) {
112+
if (configValue.get("max-level") instanceof final @NotNull String string) {
113+
if (string.startsWith("+")) {
114+
maxLevel = Integer.parseInt(string.substring(1));
115+
maxLevelRelative = true;
116+
}
117+
else {
118+
maxLevel = Integer.parseInt(string);
119+
maxLevelRelative = false;
120+
}
121+
}
122+
else {
123+
maxLevel = (Integer) configValue.get("max-level");
124+
maxLevelRelative = false;
125+
}
126+
}
127+
else {
128+
maxLevel = null;
129+
maxLevelRelative = false;
130+
}
131+
final boolean multiplyCostByLevel;
132+
final int cost;
133+
if (configValue.containsKey("cost")) {
134+
if (configValue.get("cost") instanceof final @NotNull String costString) {
135+
if (costString.startsWith("*")) {
136+
multiplyCostByLevel = true;
137+
cost = Integer.parseInt(costString.substring(1));
138+
}
139+
else {
140+
multiplyCostByLevel = false;
141+
cost = Integer.parseInt(costString);
142+
}
143+
}
144+
else {
145+
multiplyCostByLevel = false;
146+
cost = (Integer) configValue.get("cost");
147+
}
148+
}
149+
else {
150+
multiplyCostByLevel = false;
151+
cost = 0;
152+
}
153+
return new ConfigEnchantmentEntry(name, maxLevel, maxLevelRelative, cost, multiplyCostByLevel);
154+
}
155+
156+
157+
/**
158+
* From config object array
159+
*
160+
* @param configValue Config object array
161+
*/
162+
public static @NotNull List<@NotNull ConfigEnchantmentEntry> configArray(final @NotNull ArrayList<@NotNull HashMap<@NotNull String, @NotNull Object>> configValue) {
163+
return configValue.stream().map(ConfigEnchantmentEntry::configValue).collect(Collectors.toList());
164+
}
165+
166+
/**
167+
* Check if valid config object
168+
*
169+
* @param configValue Config object
170+
*/
171+
private static boolean isValidConfigValue(final @Nullable Object configValue) {
172+
if (configValue == null) return false;
173+
if (!(configValue instanceof final @NotNull ArrayList<?> arrayList)) return false;
174+
for (final @NotNull Object object : arrayList) {
175+
if (!(object instanceof final @NotNull HashMap<?, ?> hashMap)) return false;
176+
if (!hashMap.containsKey("name")) return false;
177+
if (!(hashMap.get("name") instanceof String)) return false;
178+
if (hashMap.containsKey("max-level") && !(hashMap.get("max-level") instanceof String) && !(hashMap.get("max-level") instanceof Integer))
179+
return false;
180+
if (hashMap.containsKey("cost") && !(hashMap.get("cost") instanceof String) && !(hashMap.get("cost") instanceof Integer))
181+
return false;
182+
}
183+
return true;
184+
}
185+
186+
/**
187+
* From config
188+
*
189+
* @param configValue Config object
190+
*/
191+
public static @NotNull List<@NotNull ConfigEnchantmentEntry> config(final @Nullable Object configValue) throws IllegalArgumentException {
192+
if (!isValidConfigValue(configValue)) throw new IllegalArgumentException("Invalid config value");
193+
return configArray((ArrayList<HashMap<String, Object>>) configValue);
194+
}
195+
}

src/main/java/pro/cloudnode/smp/enchantbookplus/EnchantBookPlus.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,71 @@
11
package pro.cloudnode.smp.enchantbookplus;
22

3+
import org.bukkit.enchantments.Enchantment;
34
import org.bukkit.plugin.java.JavaPlugin;
5+
import org.jetbrains.annotations.NotNull;
46
import pro.cloudnode.smp.enchantbookplus.event.PrepareAnvil;
57

8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Optional;
11+
import java.util.logging.Level;
12+
613
public final class EnchantBookPlus extends JavaPlugin {
14+
/**
15+
* Get plugin instance.
16+
*/
17+
public static EnchantBookPlus getInstance() {
18+
return getPlugin(EnchantBookPlus.class);
19+
}
20+
721
/**
822
* Register event listeners.
923
*/
1024
private void registerEvents() {
1125
getServer().getPluginManager().registerEvents(new PrepareAnvil(), this);
1226
}
1327

28+
/**
29+
* Config enchantments cache
30+
*/
31+
private @NotNull List<@NotNull ConfigEnchantmentEntry> configEnchantments = new ArrayList<>();
32+
33+
/**
34+
* Config enchantments cache
35+
*/
36+
public @NotNull List<@NotNull ConfigEnchantmentEntry> getConfigEnchantments() {
37+
if (configEnchantments.size() == 0) reload();
38+
return configEnchantments;
39+
}
40+
41+
/**
42+
* Get enchantment from cache
43+
*
44+
* @param enchantment The Minecraft enchantment
45+
*/
46+
public @NotNull Optional<@NotNull ConfigEnchantmentEntry> getConfigEnchantment(final @NotNull Enchantment enchantment) {
47+
return getConfigEnchantments().stream().filter(c -> c.isEnchantment(enchantment)).findFirst();
48+
}
49+
50+
/**
51+
* Reload config
52+
*/
53+
public void reload() {
54+
reloadConfig();
55+
try {
56+
configEnchantments = ConfigEnchantmentEntry.config(getConfig().get("enchantments"));
57+
}
58+
catch (final @NotNull Exception exception) {
59+
getLogger().log(Level.SEVERE, "Failed to load config", exception);
60+
getServer().getPluginManager().disablePlugin(this);
61+
}
62+
}
63+
1464
@Override
1565
public void onEnable() {
1666
registerEvents();
67+
saveDefaultConfig();
68+
reload();
1769
}
1870

1971
@Override

src/main/java/pro/cloudnode/smp/enchantbookplus/event/PrepareAnvil.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@
99
import org.bukkit.inventory.ItemStack;
1010
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
1111
import org.jetbrains.annotations.NotNull;
12+
import pro.cloudnode.smp.enchantbookplus.ConfigEnchantmentEntry;
13+
import pro.cloudnode.smp.enchantbookplus.EnchantBookPlus;
1214

13-
import java.util.*;
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
import java.util.Objects;
18+
import java.util.Optional;
1419

15-
public class PrepareAnvil implements Listener {
20+
public final class PrepareAnvil implements Listener {
1621
@EventHandler
1722
public void onPrepareAnvil(final @NotNull PrepareAnvilEvent event) {
1823
final @NotNull Optional<@NotNull ItemStack> result = Optional.ofNullable(event.getResult());
@@ -26,27 +31,41 @@ public void onPrepareAnvil(final @NotNull PrepareAnvilEvent event) {
2631
final @NotNull Map<@NotNull Enchantment, @NotNull Integer> upgradeEnchants =
2732
upgrade.getType() == Material.ENCHANTED_BOOK && upgrade.getItemMeta() instanceof final @NotNull EnchantmentStorageMeta upgradeMeta ?
2833
upgradeMeta.getStoredEnchants() : upgrade.getEnchantments();
29-
if (itemEnchants.isEmpty() || upgradeEnchants.isEmpty()) return;
34+
if (upgradeEnchants.isEmpty()) return;
3035
final @NotNull HashMap<@NotNull Enchantment, @NotNull Integer> upgrades = new HashMap<>();
36+
int cost = 0;
3137
for (final @NotNull Map.Entry<@NotNull Enchantment, @NotNull Integer> entry : upgradeEnchants.entrySet()) {
3238
final @NotNull Enchantment enchantment = entry.getKey();
39+
if (enchantment.getMaxLevel() == 1) continue;
40+
final @NotNull Optional<@NotNull ConfigEnchantmentEntry> configEnchantment = EnchantBookPlus.getInstance().getConfigEnchantment(enchantment);
41+
if (configEnchantment.isEmpty()) continue;
3342
final int upgradeLevel = entry.getValue();
34-
if (upgradeLevel <= enchantment.getMaxLevel()) continue;
43+
final int finalLevel;
3544
if (itemEnchants.containsKey(enchantment)) {
3645
final int itemLevel = itemEnchants.get(enchantment);
37-
if (itemLevel > upgradeLevel) upgrades.put(enchantment, itemLevel);
38-
else if (itemLevel == upgradeLevel) upgrades.put(enchantment, upgradeLevel + 1);
39-
else upgrades.put(enchantment, upgradeLevel);
46+
if (itemLevel > upgradeLevel) finalLevel = itemLevel;
47+
else if (itemLevel == upgradeLevel) finalLevel = upgradeLevel + 1;
48+
else finalLevel = upgradeLevel;
4049
}
41-
else upgrades.put(enchantment, upgradeLevel);
50+
else finalLevel = upgradeLevel;
51+
if (finalLevel < enchantment.getMaxLevel()) continue;
52+
if (configEnchantment.get().getMaxLevel(enchantment).isPresent() && finalLevel > configEnchantment.get().getMaxLevel(enchantment).get()) continue;
53+
if (finalLevel > upgradeLevel) cost += configEnchantment.get().getMultiplyCostByLevel() ? configEnchantment.get().getCost() * (finalLevel - enchantment.getMaxLevel()) : configEnchantment.get().getCost();
54+
upgrades.put(enchantment, finalLevel);
4255
}
4356
if (upgrades.isEmpty()) return;
57+
final int vanillaCost = inventory.getRepairCost();
58+
inventory.setRepairCost(vanillaCost + cost);
4459
for (final @NotNull Map.Entry<@NotNull Enchantment, @NotNull Integer> entry : upgrades.entrySet()) {
4560
if (result.get().getItemMeta() instanceof final @NotNull EnchantmentStorageMeta resultMeta) {
61+
if (!resultMeta.getStoredEnchants().containsKey(entry.getKey())) continue;
4662
resultMeta.addStoredEnchant(entry.getKey(), entry.getValue(), true);
4763
result.get().setItemMeta(resultMeta);
4864
}
49-
else result.get().addUnsafeEnchantment(entry.getKey(), entry.getValue());
65+
else {
66+
if (!result.get().getEnchantments().containsKey(entry.getKey())) continue;
67+
result.get().addUnsafeEnchantment(entry.getKey(), entry.getValue());
68+
}
5069
}
5170
}
5271
}

src/main/resources/config.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# List of enchantments for which to enable upgrading above the vanilla max levels using an anvil.
2+
enchantments:
3+
# Enchantment name (same as in the /enchant command).
4+
# Set this to "ALL" to enable upgrading for all other enchantments not listed here.
5+
# 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.
6+
- name: efficiency
7+
# Maximum level to which the enchantment can be upgraded.
8+
# 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.
9+
# If you use "+" make sure to set the value in quotes, e.g. "+1" instead of +1
10+
# To allow upgrading to any level, do not set this value. Note: enchantment levels above 255 are not supported by Minecraft.
11+
max-level: 10
12+
13+
# The XP cost (in levels) added to the vanilla cost
14+
# 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)
15+
# If you use "*" make sure to set the value in quotes, e.g. "*10" instead of *10
16+
# You can also just provide a flat value for all levels, e.g. "10" will cost 10 XP levels regardless of the enchantment level
17+
cost: "*150"
18+
19+
- name: ALL
20+
max-level: "+1"
21+
cost: 100

0 commit comments

Comments
 (0)