Merge pull request #5 from bonn2/feature-mutually-exclusive-enchants

Add mutuallyExclusiveEnchantments config option
This commit is contained in:
Pim van der Loos 2022-03-13 19:24:13 +01:00 committed by GitHub
commit ccdd5b9af1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 120 additions and 65 deletions

12
pom.xml
View File

@ -24,17 +24,19 @@
</properties>
<repositories>
<repository>
<id>apache.snapshots</id>
<url>https://repository.apache.org/snapshots/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>apache.snapshots</id>
<url>https://repository.apache.org/snapshots/</url>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>

View File

@ -11,10 +11,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.*;
import java.util.logging.Level;
public class ConfigLoader
@ -37,7 +34,7 @@ public class ConfigLoader
private boolean useTierDurability;
private boolean dropNetheriteAsChestplate;
private LinkedHashSet<Enchantment> allowedEnchantments;
private boolean allowMultipleProtectionEnchantments;
private List<List<Enchantment>> mutuallyExclusiveEnchantments;
private boolean craftingInSmithingTable;
private boolean allowUpgradeToNetherite;
private boolean bypassWearPerm;
@ -81,11 +78,22 @@ public class ConfigLoader
{
"List of enchantments that are allowed to be put on an armored elytra.",
"If you do not want to allow any enchantments at all, remove them all and add \"NONE\"",
"You can find supported enchantments here:",
"https://github.com/PimvanderLoos/ArmoredElytra/blob/master/vanillaEnchantments",
"You can find supported enchantments by running the command:",
"\"armoredelytra listAvailableEnchantments\" in console",
"If you install additional enchantment plugins, you can add their enchantments as well.",
"Just add their 'NamespacedKey'. Ask the enchantment plugin dev for more info if you need it."
};
String[] mutuallyExclusiveEnchantmentsComment =
{
"The lists of enchantments that are mutually exclusive.",
"Each group [] on this list is treated as mutually exclusive, so only one of them can be on an ArmoredElytra.",
"The default follows modern vanilla rules by making the different types of protection mutually exclusive.",
"If you do not want any enchantments to be mutually exclusive, replace all the entries in this list with \"[]\"",
"You can find supported enchantments by running the command:",
"\"armoredelytra listAvailableEnchantments\" in console",
"If you install additional enchant plugins, you can make their enchantments mutually exclusive as well.",
"Just add their 'NamespacedKey'. Ask the enchantment plugin dev for more info if you need it."
};
String[] dropNetheriteAsChestplateComment =
{
"Whether to drop Netherite Armored Elytras as netherite chestplates when they are dropped",
@ -120,16 +128,6 @@ public class ConfigLoader
{
"Specify a language file to be used."
};
String[] allowMultipleProtectionEnchantmentsComment =
{
"Allow more than 1 type of protection enchantment on a single armored elytra. ",
"If true, you could have both blast protection and environmental protection at the same time.",
"If false, the second enchantment (while crafting) will override the first. So combining an armored",
"elytra that has the protection enchantment with an enchanted book that " +
"has the blast protection enchantment",
"would result in removal of the protection enchantment and addition of the " +
"blast protection enchantment."
};
String[] permissionsComment =
{
"Globally bypass permissions for wearing and/or crafting armored elytras.",
@ -164,6 +162,14 @@ public class ConfigLoader
"minecraft:thorns", "minecraft:binding_curse", "minecraft:vanishing_curse",
"minecraft:mending"));
// Set a default list of lists of mutually exclusive enchantments
// Default only has a list for the protection enchantments
List<List<String>> defaultMutuallyExclusiveEnchantments = new ArrayList<>();
defaultMutuallyExclusiveEnchantments.add(List.of("minecraft:protection",
"minecraft:projectile_protection",
"minecraft:blast_protection",
"minecraft:fire_protection"));
FileConfiguration config = plugin.getConfig();
@ -199,8 +205,11 @@ public class ConfigLoader
allowedEnchantments = new LinkedHashSet<>();
defaultAllowedEnchantments.forEach(this::addNameSpacedKey);
allowMultipleProtectionEnchantments = addNewConfigOption(config, "allowMultipleProtectionEnchantments", false,
allowMultipleProtectionEnchantmentsComment);
defaultMutuallyExclusiveEnchantments = addNewConfigOption(config, "mutuallyExclusiveEnchantments",
defaultMutuallyExclusiveEnchantments, mutuallyExclusiveEnchantmentsComment);
mutuallyExclusiveEnchantments = new LinkedList<>();
defaultMutuallyExclusiveEnchantments.forEach(this::addMutuallyExclusiveEnchantments);
allowAddingEnchantments = addNewConfigOption(config, "allowAddingEnchantments", true,
allowAddingEnchantmentsComment);
allowRenaming = addNewConfigOption(config, "allowRenaming", true, allowRenamingComment);
@ -244,6 +253,35 @@ public class ConfigLoader
}
}
private void addMutuallyExclusiveEnchantments(List<String> fullKeys)
{
List<Enchantment> enchantments = new LinkedList<>();
for (String fullKey : fullKeys) {
try {
final String[] keyParts = fullKey.strip().split(":", 2);
if (keyParts.length < 2)
{
Bukkit.getLogger().warning("\"" + fullKey + "\" is not a valid NamespacedKey!");
return;
}
//noinspection deprecation
final NamespacedKey key = new NamespacedKey(keyParts[0], keyParts[1]);
final Enchantment enchantment = Enchantment.getByKey(key);
if (enchantment == null)
{
Bukkit.getLogger().warning("The enchantment \"" + fullKey + "\" could not be found!");
return;
}
enchantments.add(enchantment);
}
catch (Exception e)
{
plugin.getLogger().log(Level.WARNING, e, () -> "Failed to register NamespacedKey key: '" + fullKey + "'");
}
}
mutuallyExclusiveEnchantments.add(enchantments);
}
private <T> T addNewConfigOption(FileConfiguration config, String optionName, T defaultValue, String[] comment)
{
ConfigOption<T> option = new ConfigOption<>(plugin, config, optionName, defaultValue, comment);
@ -318,11 +356,6 @@ public class ConfigLoader
return repairCounts[armorTier.ordinal()];
}
public boolean allowMultipleProtectionEnchantments()
{
return allowMultipleProtectionEnchantments;
}
public boolean allowRenaming()
{
return allowRenaming;
@ -358,6 +391,11 @@ public class ConfigLoader
return allowedEnchantments;
}
public List<List<Enchantment>> getMutuallyExclusiveEnchantments()
{
return mutuallyExclusiveEnchantments;
}
public boolean bypassWearPerm()
{
return bypassWearPerm;

View File

@ -6,11 +6,7 @@ import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.*;
public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Integer>>
{
@ -27,6 +23,7 @@ public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Int
{
this(enchantments);
filter(plugin.getConfigLoader().allowedEnchantments());
filterMutuallyExclusive();
}
/**
@ -95,6 +92,27 @@ public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Int
return new EnchantmentContainer(meta.getStoredEnchants(), plugin);
}
/**
* Returns a list of all enchantments that are mutually exclusive with the provided enchantment.
* <br>This does <b>not</b> include the provided enchantment
*
* @param enchantment The enchantment to get the mutually exclusives of
* @return A linked list containing all the mutually exclusive enchantments
*/
private static List<Enchantment> getMutuallyExclusiveEnchantments(Enchantment enchantment)
{
List<Enchantment> enchantments = new LinkedList<>();
for (List<Enchantment> mutuallyExclusiveEnchantments : ArmoredElytra.getInstance().getConfigLoader().getMutuallyExclusiveEnchantments())
for (Enchantment mutuallyExclusiveEnchantment : mutuallyExclusiveEnchantments)
if (mutuallyExclusiveEnchantment.equals(enchantment))
{
enchantments.addAll(mutuallyExclusiveEnchantments.stream()
.filter(i -> !i.equals(enchantment)).toList());
break;
}
return enchantments;
}
/**
* Gets the total number of enchantments in this container.
*
@ -116,6 +134,21 @@ public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Int
enchantments.keySet().retainAll(allowed);
}
/**
* Remove any entries from the list of enchantments that are mutually exclusive to each other.
* <br>First instance of a mutually exclusive enchantment gets priority
*/
public void filterMutuallyExclusive()
{
final List<Enchantment> disallowedEnchantments = new LinkedList<>();
for (Map.Entry<Enchantment, Integer> entry : enchantments.entrySet())
{
if (disallowedEnchantments.contains(entry.getKey())) continue;
disallowedEnchantments.addAll(getMutuallyExclusiveEnchantments(entry.getKey()));
}
disallowedEnchantments.forEach(enchantments.keySet()::remove);
}
/**
* Applies the enchantments to an itemstack.
*
@ -123,6 +156,10 @@ public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Int
*/
public void applyEnchantments(final ItemStack is)
{
// Clear enchantments before applying new ones
for (Enchantment enchantment : is.getEnchantments().keySet())
is.removeEnchantment(enchantment);
is.addUnsafeEnchantments(enchantments);
}
@ -182,10 +219,17 @@ public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Int
if (first == null || first.isEmpty())
return second;
final List<Enchantment> blackList =
second.keySet().stream()
.flatMap(ench -> getMutuallyExclusiveEnchantments(ench).stream())
.toList();
blackList.forEach(first.keySet()::remove);
final Map<Enchantment, Integer> combined = new HashMap<>(first);
for (Map.Entry<Enchantment, Integer> entry : second.entrySet())
{
Integer enchantLevel = first.get(entry.getKey());
// Check for enchants with higher level
Integer enchantLevel = combined.get(entry.getKey());
if (enchantLevel != null)
{
if (entry.getValue().equals(enchantLevel) && entry.getValue() < entry.getKey().getMaxLevel())
@ -194,7 +238,7 @@ public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Int
enchantLevel = entry.getValue();
// If the enchantment level has changed,
if (!enchantLevel.equals(first.get(entry.getKey())))
if (!enchantLevel.equals(combined.get(entry.getKey())))
{
combined.remove(entry.getKey());
combined.put(entry.getKey(), enchantLevel);
@ -204,35 +248,6 @@ public class EnchantmentContainer implements Iterable<Map.Entry<Enchantment, Int
combined.put(entry.getKey(), entry.getValue());
}
if (!ArmoredElytra.getInstance().getConfigLoader().allowMultipleProtectionEnchantments())
{
// Get the protection enchantment rating for both enchantment sets.
int protVal0 = Util.getProtectionEnchantmentsVal(first);
int protVal1 = Util.getProtectionEnchantmentsVal(second);
// If they have different protection enchantments, keep enchantment1's enchantments
// And remove the protection enchantment from enchantments0.
if (protVal0 != 0 && protVal1 != 0 && protVal0 != protVal1)
switch (protVal0)
{
case 1:
combined.remove(Enchantment.PROTECTION_ENVIRONMENTAL);
break;
case 2:
combined.remove(Enchantment.PROTECTION_EXPLOSIONS);
break;
case 4:
combined.remove(Enchantment.PROTECTION_FALL);
break;
case 8:
combined.remove(Enchantment.PROTECTION_FIRE);
break;
case 16:
combined.remove(Enchantment.PROTECTION_PROJECTILE);
break;
}
}
return combined;
}