Improves various things
Gets rid of XMaterial Updates API version to 16 Sets custom_model_data values to allow custom textures for each armored elytra Extracts and renames interfaces and classes used for the armored elytra builder
This commit is contained in:
parent
80ed7838d9
commit
5730d41bc6
@ -7,7 +7,6 @@ import nl.pim16aap2.armoredElytra.util.Action;
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||
import nl.pim16aap2.armoredElytra.util.Util;
|
||||
import nl.pim16aap2.armoredElytra.util.XMaterial;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -81,7 +80,7 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener {
|
||||
// Pick the block action, as that would repair the elytra by default (vanilla).
|
||||
// Also block Armored Elytra + Elytra and Elytra + Membrane
|
||||
if (itemOneTier != ArmorTier.LEATHER && matTwo == Material.LEATHER || matTwo == Material.ELYTRA ||
|
||||
matTwo.equals(XMaterial.PHANTOM_MEMBRANE.parseMaterial())) {
|
||||
matTwo.equals(Material.PHANTOM_MEMBRANE)) {
|
||||
return Action.BLOCK;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package nl.pim16aap2.armoredElytra.handlers;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder.ArmoredElytraBuilder;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
|
||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||
|
@ -1,7 +1,7 @@
|
||||
package nl.pim16aap2.armoredElytra.handlers;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder.ArmoredElytraBuilder;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
|
@ -140,7 +140,7 @@ public class EventHandlers implements Listener {
|
||||
plugin.usageDeniedMessage(e.getPlayer(), armorTier);
|
||||
e.setCancelled(true);
|
||||
}
|
||||
case ALLOWED, default -> {
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,451 +0,0 @@
|
||||
package nl.pim16aap2.armoredElytra.nbtEditor;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
|
||||
import nl.pim16aap2.armoredElytra.util.Util;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"unused", "UnusedReturnValue"})
|
||||
public class ArmoredElytraBuilder {
|
||||
private final NBTEditor nbtEditor;
|
||||
private final DurabilityManager durabilityManager;
|
||||
private final ConfigLoader config;
|
||||
private final ArmoredElytra plugin;
|
||||
|
||||
public ArmoredElytraBuilder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
|
||||
ConfigLoader config, ArmoredElytra plugin) {
|
||||
|
||||
this.nbtEditor = nbtEditor;
|
||||
this.durabilityManager = durabilityManager;
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder for an armored elytra.
|
||||
*
|
||||
* @return The first step of the new builder.
|
||||
*/
|
||||
public IStep0 newBuilder() {
|
||||
return new Builder(nbtEditor, durabilityManager, config, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for repairing an armored elytra.
|
||||
*
|
||||
* @param armoredElytra The armored elytra to repair.
|
||||
* @param repairItems The repair item(s) to use for repairing the armored elytra. It is assumed that they are of
|
||||
* the correct type.
|
||||
* @param name The new name of the output armored elytra. When this is null, {@link
|
||||
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public @Nullable ItemStack repair(ItemStack armoredElytra, ItemStack repairItems, @Nullable String name) {
|
||||
return newBuilder().ofElytra(armoredElytra).repair(repairItems.getAmount()).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for enchanting an armored elytra.
|
||||
*
|
||||
* @param armoredElytra The armored elytra to repair.
|
||||
* @param sourceItem The source item from which to copy the enchantments from.
|
||||
* @param name The new name of the output armored elytra. When this is null, {@link
|
||||
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public @Nullable ItemStack enchant(ItemStack armoredElytra, ItemStack sourceItem, @Nullable String name) {
|
||||
final EnchantmentContainer enchantments = EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin);
|
||||
if (enchantments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return newBuilder().ofElytra(armoredElytra).addEnchantments(enchantments).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for creating a new armored elytra from two items.
|
||||
*
|
||||
* @param elytra The input item. This should be an (armored) elytra.
|
||||
* @param combiner The item to combine with the elytra. This should either be an armored elytra of the same
|
||||
* non-NONE tier as the input elytra or a chestplate.
|
||||
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combine(ItemStack,
|
||||
* ItemStack, String)} instead.
|
||||
* @param name The new name of the output armored elytra. When this is null, {@link
|
||||
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public ItemStack combine(ItemStack elytra, ItemStack combiner, ArmorTier armorTier, @Nullable String name) {
|
||||
return newBuilder().ofElytra(elytra).combineWith(combiner, armorTier).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #combine(ItemStack, ItemStack, ArmorTier, String)} for unknown armor tiers.
|
||||
*/
|
||||
public ItemStack combine(ItemStack elytra, ItemStack combiner, @Nullable String name) {
|
||||
return newBuilder().ofElytra(elytra).combineWith(combiner).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new armored elytra of a specific tier.
|
||||
*
|
||||
* @param armorTier The tier of the new armored elytra.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public ItemStack newArmoredElytra(ArmorTier armorTier) {
|
||||
return newBuilder().newItem(armorTier).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the third and last step of the armored elytra build process.
|
||||
*/
|
||||
public interface IStep2 {
|
||||
/**
|
||||
* Specifies the new name of the armored elytra.
|
||||
*
|
||||
* @param name The new name of the armored elytra. When this is null (default), the default name for the new
|
||||
* tier will be used.
|
||||
* @return The current builder step.
|
||||
*/
|
||||
IStep2 withName(@Nullable String name);
|
||||
|
||||
/**
|
||||
* Specifies the new color of the armored elytra.
|
||||
*
|
||||
* @param color The new color of the armored elytra. When this is null (default), the color to use is inferred
|
||||
* from the creation process.
|
||||
* @return The current builder step.
|
||||
*/
|
||||
IStep2 withColor(@Nullable Color color);
|
||||
|
||||
/**
|
||||
* Specifies the new lore of the armored elytra.
|
||||
*
|
||||
* @param lore The new lore of the armored elytra. When this is null (default), the default lore for the new
|
||||
* tier will be used.
|
||||
* @return The current builder step.
|
||||
*/
|
||||
IStep2 withLore(@Nullable List<String> lore);
|
||||
|
||||
/**
|
||||
* Specifies whether the armored elytra should be unbreakable.
|
||||
* <p>
|
||||
* By default, this value is read from {@link ConfigLoader#unbreakable()}.
|
||||
*
|
||||
* @param isUnbreakable True if the armored elytra should be unbreakable.
|
||||
* @return The current builder step.
|
||||
*/
|
||||
IStep2 unbreakable(boolean isUnbreakable);
|
||||
|
||||
/**
|
||||
* Constructs the armored elytra from the provided configuration.
|
||||
*
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
ItemStack build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the second and last step of the armored elytra build process.
|
||||
*/
|
||||
public interface IStep1 {
|
||||
/**
|
||||
* Repairs the armored elytra provided as input.
|
||||
*
|
||||
* @param count The amount of repair items to process.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep2 repair(int count);
|
||||
|
||||
/**
|
||||
* Adds a set of enchantments to the armored elytra.
|
||||
*
|
||||
* @param enchantmentContainer The enchantments to add.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep2 addEnchantments(EnchantmentContainer enchantmentContainer);
|
||||
|
||||
/**
|
||||
* Adds a set of enchantments to the armored elytra.
|
||||
*
|
||||
* @param sourceItem The source item from which to take the enchantments.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep2 addEnchantments(ItemStack sourceItem);
|
||||
|
||||
/**
|
||||
* Combines the input elytra with another item.
|
||||
*
|
||||
* @param item The item to combine with the input elytra. This can either be a chestplate or, if the input
|
||||
* elytra is an armored one, another armored elytra.
|
||||
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combineWith(ItemStack)}
|
||||
* instead.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep2 combineWith(ItemStack item, ArmorTier armorTier);
|
||||
|
||||
/**
|
||||
* See {@link #combineWith(ItemStack, ArmorTier)}. Used when the armor tier of the input item is not known.
|
||||
*
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep2 combineWith(ItemStack item);
|
||||
|
||||
/**
|
||||
* Upgrades the elytra to a specific armor tier.
|
||||
*
|
||||
* @param armorTier The new armor tier.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep2 upgradeToTier(ArmorTier armorTier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the first and last step of the armored elytra build process.
|
||||
*/
|
||||
public interface IStep0 {
|
||||
/**
|
||||
* Use an elytra as base item to create the new armored elytra from.
|
||||
*
|
||||
* @param elytra An itemstack that represents an elytra. It does not matter whether the elytra is armored or
|
||||
* not.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep1 ofElytra(ItemStack elytra);
|
||||
|
||||
/**
|
||||
* Creates a fresh new armored elytra of a specific tier.
|
||||
*
|
||||
* @param armorTier The tier of the new armored elytra.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
IStep2 newItem(ArmorTier armorTier);
|
||||
}
|
||||
|
||||
private static final class Builder implements IStep0, IStep1, IStep2 {
|
||||
private static final Color DEFAULT_LEATHER_COLOR = Bukkit.getServer().getItemFactory().getDefaultLeatherColor();
|
||||
|
||||
private final NBTEditor nbtEditor;
|
||||
private final DurabilityManager durabilityManager;
|
||||
private final ConfigLoader config;
|
||||
private final ArmoredElytra plugin;
|
||||
|
||||
// These aren't nullable, as they are set by the only entry points.
|
||||
/**
|
||||
* The new armored elytra that will be returned at the end of the build process.
|
||||
*/
|
||||
private ItemStack newArmoredElytra;
|
||||
/**
|
||||
* The combined enchantments of the input items.
|
||||
*/
|
||||
private EnchantmentContainer combinedEnchantments;
|
||||
/**
|
||||
* The current armor tier of the input elytra.
|
||||
*/
|
||||
private ArmorTier currentArmorTier;
|
||||
/**
|
||||
* The durability of the output armored elytra.
|
||||
*/
|
||||
private int durability;
|
||||
|
||||
|
||||
/**
|
||||
* The armor tier of the output armored elytra. This defaults to {@link #currentArmorTier} if this isn't set.
|
||||
*/
|
||||
private @Nullable ArmorTier newArmorTier;
|
||||
/**
|
||||
* The name of the output armored elytra. This defaults to {@link ArmoredElytra#getArmoredElytraName(ArmorTier)}
|
||||
* when not overridden.
|
||||
*/
|
||||
private @Nullable String name;
|
||||
/**
|
||||
* The lore of the output armored elytra. This defaults to {@link ArmoredElytra#getElytraLore(ArmorTier)} when
|
||||
* not overridden.
|
||||
*/
|
||||
private @Nullable List<String> lore;
|
||||
/**
|
||||
* The color of the output armored elytra. By default, the existing color (if any is used). When combined with
|
||||
* another item, the color is inferred using {@link #getItemColor(ItemStack, ItemStack)}.
|
||||
*/
|
||||
private @Nullable Color color;
|
||||
/**
|
||||
* Whether the output armored elytra should be unbreakable. This defaults to {@link ConfigLoader#unbreakable()}
|
||||
* when not overridden.
|
||||
*/
|
||||
private @Nullable Boolean isUnbreakable = null;
|
||||
|
||||
private Builder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
|
||||
ConfigLoader config, ArmoredElytra plugin) {
|
||||
this.nbtEditor = nbtEditor;
|
||||
this.durabilityManager = durabilityManager;
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack build() {
|
||||
// Get default values if unset.
|
||||
newArmorTier = newArmorTier == null ? currentArmorTier : newArmorTier;
|
||||
name = name == null ? plugin.getArmoredElytraName(newArmorTier) : name;
|
||||
lore = lore == null ? plugin.getElytraLore(newArmorTier) : lore;
|
||||
|
||||
isUnbreakable = isUnbreakable == null ? config.unbreakable() : isUnbreakable;
|
||||
|
||||
final ItemStack output = nbtEditor.addArmorNBTTags(newArmoredElytra, newArmorTier,
|
||||
isUnbreakable, name, lore, color);
|
||||
durabilityManager.setDurability(output, durability, newArmorTier);
|
||||
combinedEnchantments.applyEnchantments(output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 withName(@Nullable String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 withColor(@Nullable Color color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 withLore(@Nullable List<String> lore) {
|
||||
this.lore = lore;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 unbreakable(boolean isUnbreakable) {
|
||||
this.isUnbreakable = isUnbreakable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 repair(int count) {
|
||||
if (currentArmorTier == ArmorTier.NONE) {
|
||||
throw new IllegalArgumentException("Non-armored elytras cannot be repaired!");
|
||||
}
|
||||
durability = durabilityManager.getRepairedDurability(newArmoredElytra, count, currentArmorTier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 addEnchantments(EnchantmentContainer enchantmentContainer) {
|
||||
combinedEnchantments.merge(enchantmentContainer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 addEnchantments(ItemStack sourceItem) {
|
||||
return addEnchantments(EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 combineWith(ItemStack item, ArmorTier armorTier) {
|
||||
if (armorTier == ArmorTier.NONE && !Util.isChestPlate(item)) {
|
||||
throw new IllegalArgumentException("Non-armored elytras can only be combined with chest plates!");
|
||||
}
|
||||
|
||||
newArmorTier = armorTier;
|
||||
if (currentArmorTier == ArmorTier.NONE &&
|
||||
item.getType().equals(Material.ELYTRA) && newArmorTier != ArmorTier.NONE) {
|
||||
throw new IllegalArgumentException("A regular elytra cannot be combined with an armored one!");
|
||||
}
|
||||
|
||||
withColor(getItemColor(newArmoredElytra, item));
|
||||
|
||||
addEnchantments(item);
|
||||
|
||||
durability = durabilityManager.getCombinedDurability(newArmoredElytra, item,
|
||||
currentArmorTier, newArmorTier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 combineWith(ItemStack item) {
|
||||
final ArmorTier armorTier = item.getType().equals(Material.ELYTRA) ?
|
||||
nbtEditor.getArmorTier(item) : Util.armorToTier(item.getType());
|
||||
return combineWith(item, armorTier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 upgradeToTier(ArmorTier armorTier) {
|
||||
newArmorTier = armorTier;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep1 ofElytra(ItemStack elytra) {
|
||||
if (!elytra.getType().equals(Material.ELYTRA)) {
|
||||
throw new IllegalArgumentException("Expected elytra as input, but got: " + elytra);
|
||||
}
|
||||
|
||||
newArmoredElytra = new ItemStack(elytra);
|
||||
|
||||
if (currentArmorTier == null) {
|
||||
currentArmorTier = nbtEditor.getArmorTier(elytra);
|
||||
}
|
||||
|
||||
combinedEnchantments = EnchantmentContainer.getEnchantmentsOf(newArmoredElytra, plugin);
|
||||
|
||||
durability = durabilityManager.getRealDurability(newArmoredElytra, currentArmorTier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IStep2 newItem(ArmorTier armorTier) {
|
||||
currentArmorTier = newArmorTier = armorTier;
|
||||
ofElytra(new ItemStack(Material.ELYTRA));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of the item if the item has a color.
|
||||
* <p>
|
||||
* See {@link LeatherArmorMeta#getColor()}.
|
||||
*
|
||||
* @param itemA The first {@link ItemStack} to check.
|
||||
* @param itemB The second {@link ItemStack} to check.
|
||||
* @return The color of the item, if it has a color, otherwise null.
|
||||
*/
|
||||
private @Nullable Color getItemColor(final ItemStack itemA, final ItemStack itemB) {
|
||||
final @Nullable Color colorA = getItemColor(itemA);
|
||||
if (colorA != null && !colorA.equals(DEFAULT_LEATHER_COLOR)) {
|
||||
return colorA;
|
||||
}
|
||||
|
||||
final @Nullable Color colorB = getItemColor(itemB);
|
||||
return colorB != null ? colorB : colorA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the colors of an item if available.
|
||||
* <p>
|
||||
* This currently only applies to leather armor(ed elytras).
|
||||
*
|
||||
* @param itemStack The item to analyze.
|
||||
* @return The color of the item, if available, otherwise null.
|
||||
*/
|
||||
private @Nullable Color getItemColor(final ItemStack itemStack) {
|
||||
if (itemStack.getType() == Material.ELYTRA) {
|
||||
return nbtEditor.getColorOfArmoredElytra(itemStack);
|
||||
}
|
||||
|
||||
if (!itemStack.hasItemMeta() || !(itemStack.getItemMeta() instanceof LeatherArmorMeta)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ((LeatherArmorMeta) itemStack.getItemMeta()).getColor();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@SuppressWarnings({"unused", "UnusedReturnValue"})
|
||||
public class ArmoredElytraBuilder {
|
||||
private final NBTEditor nbtEditor;
|
||||
private final DurabilityManager durabilityManager;
|
||||
private final ConfigLoader config;
|
||||
private final ArmoredElytra plugin;
|
||||
|
||||
public ArmoredElytraBuilder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
|
||||
ConfigLoader config, ArmoredElytra plugin) {
|
||||
|
||||
this.nbtEditor = nbtEditor;
|
||||
this.durabilityManager = durabilityManager;
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new builder for an armored elytra.
|
||||
*
|
||||
* @return The first step of the new builder.
|
||||
*/
|
||||
public ItemCreator newBuilder() {
|
||||
return new Builder(nbtEditor, durabilityManager, config, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for repairing an armored elytra.
|
||||
*
|
||||
* @param armoredElytra The armored elytra to repair.
|
||||
* @param repairItems The repair item(s) to use for repairing the armored elytra. It is assumed that they are of
|
||||
* the correct type.
|
||||
* @param name The new name of the output armored elytra. When this is null, {@link
|
||||
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public @Nullable ItemStack repair(ItemStack armoredElytra, ItemStack repairItems, @Nullable String name) {
|
||||
return newBuilder().ofElytra(armoredElytra).repair(repairItems.getAmount()).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for enchanting an armored elytra.
|
||||
*
|
||||
* @param armoredElytra The armored elytra to repair.
|
||||
* @param sourceItem The source item from which to copy the enchantments from.
|
||||
* @param name The new name of the output armored elytra. When this is null, {@link
|
||||
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public @Nullable ItemStack enchant(ItemStack armoredElytra, ItemStack sourceItem, @Nullable String name) {
|
||||
final EnchantmentContainer enchantments = EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin);
|
||||
if (enchantments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return newBuilder().ofElytra(armoredElytra).addEnchantments(enchantments).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for creating a new armored elytra from two items.
|
||||
*
|
||||
* @param elytra The input item. This should be an (armored) elytra.
|
||||
* @param combiner The item to combine with the elytra. This should either be an armored elytra of the same
|
||||
* non-NONE tier as the input elytra or a chestplate.
|
||||
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combine(ItemStack,
|
||||
* ItemStack, String)} instead.
|
||||
* @param name The new name of the output armored elytra. When this is null, {@link
|
||||
* ArmoredElytra#getArmoredElytraName(ArmorTier)} is used to set the name.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public ItemStack combine(ItemStack elytra, ItemStack combiner, ArmorTier armorTier, @Nullable String name) {
|
||||
return newBuilder().ofElytra(elytra).combineWith(combiner, armorTier).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link #combine(ItemStack, ItemStack, ArmorTier, String)} for unknown armor tiers.
|
||||
*/
|
||||
public ItemStack combine(ItemStack elytra, ItemStack combiner, @Nullable String name) {
|
||||
return newBuilder().ofElytra(elytra).combineWith(combiner).withName(name).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new armored elytra of a specific tier.
|
||||
*
|
||||
* @param armorTier The tier of the new armored elytra.
|
||||
* @return The new armored elytra.
|
||||
*/
|
||||
public ItemStack newArmoredElytra(ArmorTier armorTier) {
|
||||
return newBuilder().newItem(armorTier).build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,258 @@
|
||||
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.DurabilityManager;
|
||||
import nl.pim16aap2.armoredElytra.nbtEditor.NBTEditor;
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
|
||||
import nl.pim16aap2.armoredElytra.util.Util;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static nl.pim16aap2.armoredElytra.util.Util.getTextureIdFromArmorTier;
|
||||
|
||||
final class Builder implements ItemCreator, ItemCombinator, ItemMetadataSetter {
|
||||
|
||||
private static final Color DEFAULT_LEATHER_COLOR = Bukkit.getServer().getItemFactory().getDefaultLeatherColor();
|
||||
|
||||
private final NBTEditor nbtEditor;
|
||||
private final DurabilityManager durabilityManager;
|
||||
private final ConfigLoader config;
|
||||
private final ArmoredElytra plugin;
|
||||
|
||||
/**
|
||||
* The new armored elytra that will be returned at the end of the build process.
|
||||
*/
|
||||
private ItemStack newArmoredElytra;
|
||||
/**
|
||||
* The combined enchantments of the input items.
|
||||
*/
|
||||
private EnchantmentContainer combinedEnchantments;
|
||||
/**
|
||||
* The current armor tier of the input elytra.
|
||||
*/
|
||||
private ArmorTier currentArmorTier;
|
||||
/**
|
||||
* The durability of the output armored elytra.
|
||||
*/
|
||||
private int durability;
|
||||
|
||||
|
||||
/**
|
||||
* The armor tier of the output armored elytra. This defaults to {@link #currentArmorTier} if this isn't set.
|
||||
*/
|
||||
private @Nullable ArmorTier newArmorTier;
|
||||
/**
|
||||
* The name of the output armored elytra. This defaults to {@link ArmoredElytra#getArmoredElytraName(ArmorTier)}
|
||||
* when not overridden.
|
||||
*/
|
||||
private @Nullable String name;
|
||||
/**
|
||||
* The lore of the output armored elytra. This defaults to {@link ArmoredElytra#getElytraLore(ArmorTier)} when
|
||||
* not overridden.
|
||||
*/
|
||||
private @Nullable List<String> lore;
|
||||
/**
|
||||
* The color of the output armored elytra. By default, the existing color (if any is used). When combined with
|
||||
* another item, the color is inferred using {@link #getItemColor(ItemStack, ItemStack)}.
|
||||
*/
|
||||
private @Nullable Color color;
|
||||
/**
|
||||
* Whether the output armored elytra should be unbreakable. This defaults to {@link ConfigLoader#unbreakable()}
|
||||
* when not overridden.
|
||||
*/
|
||||
private @Nullable Boolean isUnbreakable = null;
|
||||
/**
|
||||
* The texture id (custom_model_data) to be applied to the item. If null, no custom texture id will be set.
|
||||
*/
|
||||
private @Nullable Integer textureId = null;
|
||||
|
||||
Builder(NBTEditor nbtEditor, DurabilityManager durabilityManager,
|
||||
ConfigLoader config, ArmoredElytra plugin) {
|
||||
this.nbtEditor = nbtEditor;
|
||||
this.durabilityManager = durabilityManager;
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack build() {
|
||||
// Get default values if unset.
|
||||
newArmorTier = newArmorTier == null ? currentArmorTier : newArmorTier;
|
||||
name = name == null ? plugin.getArmoredElytraName(newArmorTier) : name;
|
||||
lore = lore == null ? plugin.getElytraLore(newArmorTier) : lore;
|
||||
|
||||
isUnbreakable = isUnbreakable == null ? config.unbreakable() : isUnbreakable;
|
||||
|
||||
final ItemStack output = nbtEditor.addArmorNBTTags(newArmoredElytra, newArmorTier,
|
||||
isUnbreakable, name, lore, color, textureId);
|
||||
durabilityManager.setDurability(output, durability, newArmorTier);
|
||||
combinedEnchantments.applyEnchantments(output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter withName(@Nullable String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter withColor(@Nullable Color color) {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter withTexture(@Nullable Integer textureId) {
|
||||
if (textureId != null) {
|
||||
this.textureId = textureId;
|
||||
} else if (newArmorTier != null) {
|
||||
this.textureId = getTextureIdFromArmorTier(newArmorTier);
|
||||
} else {
|
||||
this.textureId = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter withLore(@Nullable List<String> lore) {
|
||||
this.lore = lore;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter unbreakable(boolean isUnbreakable) {
|
||||
this.isUnbreakable = isUnbreakable;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter repair(int count) {
|
||||
if (currentArmorTier == ArmorTier.NONE) {
|
||||
throw new IllegalArgumentException("Non-armored elytras cannot be repaired!");
|
||||
}
|
||||
durability = durabilityManager.getRepairedDurability(newArmoredElytra, count, currentArmorTier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter addEnchantments(EnchantmentContainer enchantmentContainer) {
|
||||
combinedEnchantments.merge(enchantmentContainer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter addEnchantments(ItemStack sourceItem) {
|
||||
return addEnchantments(EnchantmentContainer.getEnchantmentsOf(sourceItem, plugin));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter combineWith(ItemStack item, ArmorTier armorTier) {
|
||||
// Note: This method is almost always run. Important building steps should happen here.
|
||||
|
||||
if (armorTier == ArmorTier.NONE && !Util.isChestPlate(item)) {
|
||||
throw new IllegalArgumentException("Non-armored elytras can only be combined with chest plates!");
|
||||
}
|
||||
|
||||
newArmorTier = armorTier;
|
||||
if (currentArmorTier == ArmorTier.NONE &&
|
||||
item.getType().equals(Material.ELYTRA) && newArmorTier != ArmorTier.NONE) {
|
||||
throw new IllegalArgumentException("A regular elytra cannot be combined with an armored one!");
|
||||
}
|
||||
|
||||
withColor(getItemColor(newArmoredElytra, item)).withTexture(null);
|
||||
|
||||
addEnchantments(item);
|
||||
|
||||
durability = durabilityManager.getCombinedDurability(newArmoredElytra, item,
|
||||
currentArmorTier, newArmorTier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter combineWith(ItemStack item) {
|
||||
final ArmorTier armorTier = item.getType().equals(Material.ELYTRA) ?
|
||||
nbtEditor.getArmorTier(item) : Util.armorToTier(item.getType());
|
||||
return combineWith(item, armorTier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter upgradeToTier(ArmorTier armorTier) {
|
||||
newArmorTier = armorTier;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemCombinator ofElytra(ItemStack elytra) {
|
||||
if (!elytra.getType().equals(Material.ELYTRA)) {
|
||||
throw new IllegalArgumentException("Expected elytra as input, but got: " + elytra);
|
||||
}
|
||||
|
||||
newArmoredElytra = new ItemStack(elytra);
|
||||
|
||||
if (currentArmorTier == null) {
|
||||
currentArmorTier = nbtEditor.getArmorTier(elytra);
|
||||
}
|
||||
|
||||
combinedEnchantments = EnchantmentContainer.getEnchantmentsOf(newArmoredElytra, plugin);
|
||||
|
||||
durability = durabilityManager.getRealDurability(newArmoredElytra, currentArmorTier);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemMetadataSetter newItem(ArmorTier armorTier) {
|
||||
currentArmorTier = newArmorTier = armorTier;
|
||||
ofElytra(new ItemStack(Material.ELYTRA));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color of the item if the item has a color.
|
||||
* <p>
|
||||
* See {@link LeatherArmorMeta#getColor()}.
|
||||
*
|
||||
* @param itemA The first {@link ItemStack} to check.
|
||||
* @param itemB The second {@link ItemStack} to check.
|
||||
* @return The color of the item, if it has a color, otherwise null.
|
||||
*/
|
||||
private @Nullable Color getItemColor(final ItemStack itemA, final ItemStack itemB) {
|
||||
final @Nullable Color colorA = getItemColor(itemA);
|
||||
if (colorA != null && !colorA.equals(DEFAULT_LEATHER_COLOR)) {
|
||||
return colorA;
|
||||
}
|
||||
|
||||
final @Nullable Color colorB = getItemColor(itemB);
|
||||
return colorB != null ? colorB : colorA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the colors of an item if available.
|
||||
* <p>
|
||||
* This currently only applies to leather armor(ed elytras).
|
||||
*
|
||||
* @param itemStack The item to analyze.
|
||||
* @return The color of the item, if available, otherwise null.
|
||||
*/
|
||||
private @Nullable Color getItemColor(final ItemStack itemStack) {
|
||||
if (itemStack.getType() == Material.ELYTRA) {
|
||||
return nbtEditor.getColorOfArmoredElytra(itemStack);
|
||||
}
|
||||
|
||||
if (!itemStack.hasItemMeta() || !(itemStack.getItemMeta() instanceof LeatherArmorMeta)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ((LeatherArmorMeta) itemStack.getItemMeta()).getColor();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* An interface describing the combination step of building an armored elytra
|
||||
*/
|
||||
public interface ItemCombinator {
|
||||
|
||||
/**
|
||||
* Repairs the armored elytra provided as input.
|
||||
*
|
||||
* @param count The amount of repair items to process.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
ItemMetadataSetter repair(int count);
|
||||
|
||||
/**
|
||||
* Adds a set of enchantments to the armored elytra.
|
||||
*
|
||||
* @param enchantmentContainer The enchantments to add.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
ItemMetadataSetter addEnchantments(EnchantmentContainer enchantmentContainer);
|
||||
|
||||
/**
|
||||
* Adds a set of enchantments to the armored elytra.
|
||||
*
|
||||
* @param sourceItem The source item from which to take the enchantments.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
ItemMetadataSetter addEnchantments(ItemStack sourceItem);
|
||||
|
||||
/**
|
||||
* Combines the input elytra with another item.
|
||||
*
|
||||
* @param item The item to combine with the input elytra. This can either be a chestplate or, if the input
|
||||
* elytra is an armored one, another armored elytra.
|
||||
* @param armorTier The armor tier of the input item. If this is not known, use {@link #combineWith(ItemStack)}
|
||||
* instead.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
ItemMetadataSetter combineWith(ItemStack item, ArmorTier armorTier);
|
||||
|
||||
/**
|
||||
* See {@link #combineWith(ItemStack, ArmorTier)}. Used when the armor tier of the input item is not known.
|
||||
*
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
ItemMetadataSetter combineWith(ItemStack item);
|
||||
|
||||
/**
|
||||
* Upgrades the elytra to a specific armor tier.
|
||||
*
|
||||
* @param armorTier The new armor tier.
|
||||
* @return The next step of the builder process.
|
||||
*/
|
||||
ItemMetadataSetter upgradeToTier(ArmorTier armorTier);
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* An interface describing the first step of building an armored elytra
|
||||
*/
|
||||
public interface ItemCreator {
|
||||
|
||||
/**
|
||||
* Use an elytra as base item to create the new armored elytra from.
|
||||
*
|
||||
* @param elytra <p>An item-stack that represents an elytra. It does not matter whether the elytra is armored or not.</p>
|
||||
* @return <p>The next step of the builder process.</p>
|
||||
*/
|
||||
ItemCombinator ofElytra(ItemStack elytra);
|
||||
|
||||
/**
|
||||
* Creates a fresh new armored elytra of a specific tier.
|
||||
*
|
||||
* @param armorTier <p>The tier of the new armored elytra.</p>
|
||||
* @return <p>The next step of the builder process.</p>
|
||||
*/
|
||||
ItemMetadataSetter newItem(ArmorTier armorTier);
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package nl.pim16aap2.armoredElytra.nbtEditor.ArmoredElytraBuilder;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An interface describing the last step of building an armored elytra: updating metadata
|
||||
*/
|
||||
public interface ItemMetadataSetter {
|
||||
|
||||
/**
|
||||
* Specifies the new name of the armored elytra.
|
||||
*
|
||||
* @param name The new name of the armored elytra. When this is null (default), the default name for the new
|
||||
* tier will be used.
|
||||
* @return <p>The current metadata setter.</p>
|
||||
*/
|
||||
ItemMetadataSetter withName(@Nullable String name);
|
||||
|
||||
/**
|
||||
* Specifies the new color of the armored elytra.
|
||||
*
|
||||
* @param color <p>The new color of the armored elytra. When this is null (default), the color to use is inferred
|
||||
* from the creation process.</p>
|
||||
* @return <p>The current metadata setter.</p>
|
||||
*/
|
||||
ItemMetadataSetter withColor(@Nullable Color color);
|
||||
|
||||
/**
|
||||
* Specifies the texture identifier (custom_model_data) for the armored elytra
|
||||
*
|
||||
* @param textureId <p>The texture id to set for the armored elytra. If null, the default values will be used.</p>
|
||||
* @return <p>The current metadata setter.</p>
|
||||
*/
|
||||
ItemMetadataSetter withTexture(@Nullable Integer textureId);
|
||||
|
||||
/**
|
||||
* Specifies the new lore of the armored elytra.
|
||||
*
|
||||
* @param lore The new lore of the armored elytra. When this is null (default), the default lore for the new
|
||||
* tier will be used.
|
||||
* @return <p>The current metadata setter.</p>
|
||||
*/
|
||||
//TODO: UNUSED!
|
||||
ItemMetadataSetter withLore(@Nullable List<String> lore);
|
||||
|
||||
/**
|
||||
* Specifies whether the armored elytra should be unbreakable.
|
||||
* <p>
|
||||
* By default, this value is read from {@link ConfigLoader#unbreakable()}.
|
||||
*
|
||||
* @param isUnbreakable True if the armored elytra should be unbreakable.
|
||||
* @return <p>The current metadata setter.</p>
|
||||
*/
|
||||
//TODO: UNUSED!
|
||||
ItemMetadataSetter unbreakable(boolean isUnbreakable);
|
||||
|
||||
/**
|
||||
* Constructs the armored elytra from the provided configuration.
|
||||
*
|
||||
* @return <p>The new armored elytra.</p>
|
||||
*/
|
||||
ItemStack build();
|
||||
|
||||
}
|
@ -84,42 +84,47 @@ public class NBTEditor {
|
||||
* @param name The name of the item.
|
||||
* @param lore The lore of the item.
|
||||
* @param color The color of the armor to store. May be null.
|
||||
* @param textureId <p>The id of the texture to use (custom_model_data)</p>
|
||||
* @return The NEW item.
|
||||
*/
|
||||
public ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable, String name,
|
||||
@Nullable List<String> lore, @Nullable Color color) {
|
||||
@Nullable List<String> lore, @Nullable Color color, @Nullable Integer textureId) {
|
||||
if (armorTier == null || armorTier == ArmorTier.NONE) {
|
||||
return new ItemStack(item);
|
||||
}
|
||||
|
||||
final ItemStack ret = new ItemStack(item);
|
||||
final ItemMeta meta = getOrCreateItemMeta(ret);
|
||||
meta.getPersistentDataContainer().set(ARMOR_TIER_KEY, PersistentDataType.INTEGER,
|
||||
final ItemStack returnedItem = new ItemStack(item);
|
||||
final ItemMeta itemMeta = getOrCreateItemMeta(returnedItem);
|
||||
itemMeta.getPersistentDataContainer().set(ARMOR_TIER_KEY, PersistentDataType.INTEGER,
|
||||
ArmorTier.getTierID(armorTier));
|
||||
|
||||
if (color != null && armorTier == ArmorTier.LEATHER) {
|
||||
meta.getPersistentDataContainer().set(ARMOR_COLOR_KEY, PersistentDataType.INTEGER, color.asRGB());
|
||||
itemMeta.getPersistentDataContainer().set(ARMOR_COLOR_KEY, PersistentDataType.INTEGER, color.asRGB());
|
||||
}
|
||||
|
||||
overwriteNBTValue(meta, Attribute.GENERIC_ARMOR, ArmorTier.getArmor(armorTier), "generic.armor");
|
||||
overwriteNBTValue(itemMeta, Attribute.GENERIC_ARMOR, ArmorTier.getArmor(armorTier), "generic.armor");
|
||||
if (ArmorTier.getToughness(armorTier) > 0) {
|
||||
overwriteNBTValue(meta, Attribute.GENERIC_ARMOR_TOUGHNESS, ArmorTier.getToughness(armorTier),
|
||||
overwriteNBTValue(itemMeta, Attribute.GENERIC_ARMOR_TOUGHNESS, ArmorTier.getToughness(armorTier),
|
||||
"generic.armor_toughness");
|
||||
}
|
||||
|
||||
if (ArmorTier.getKnockbackResistance(armorTier) > 0) {
|
||||
overwriteNBTValue(meta, Attribute.GENERIC_KNOCKBACK_RESISTANCE, ArmorTier.getKnockbackResistance(armorTier),
|
||||
overwriteNBTValue(itemMeta, Attribute.GENERIC_KNOCKBACK_RESISTANCE, ArmorTier.getKnockbackResistance(armorTier),
|
||||
"generic.knockback_resistance");
|
||||
}
|
||||
|
||||
meta.setUnbreakable(unbreakable);
|
||||
meta.setDisplayName(name);
|
||||
|
||||
itemMeta.setUnbreakable(unbreakable);
|
||||
itemMeta.setDisplayName(name);
|
||||
if (lore != null) {
|
||||
meta.setLore(lore);
|
||||
itemMeta.setLore(lore);
|
||||
}
|
||||
|
||||
ret.setItemMeta(meta);
|
||||
return ret;
|
||||
if (textureId != null) {
|
||||
itemMeta.setCustomModelData(textureId);
|
||||
}
|
||||
returnedItem.setItemMeta(itemMeta);
|
||||
return returnedItem;
|
||||
}
|
||||
|
||||
void overwriteNBTValue(ItemMeta meta, Attribute attribute, double value, String modifierName) {
|
||||
|
@ -6,6 +6,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum ArmorTier {
|
||||
|
||||
// Tier: TierID, armor-value, armor-toughness, knockbackResistance, repair, defaultRepairCount, name, durability
|
||||
NONE(0, 0, 0, 0, null, 0, "", 0),
|
||||
LEATHER(1, 3, 0, 0, Material.LEATHER, 6, "leather", 80),
|
||||
@ -13,7 +14,7 @@ public enum ArmorTier {
|
||||
CHAIN(3, 5, 0, 0, Material.IRON_INGOT, 4, "chain", 240),
|
||||
IRON(4, 6, 0, 0, Material.IRON_INGOT, 4, "iron", 240),
|
||||
DIAMOND(5, 8, 2, 0, Material.DIAMOND, 3, "diamond", 528),
|
||||
NETHERITE(6, 8, 3, 0.1, XMaterial.NETHERITE_INGOT.parseMaterial(), 3, "netherite", 592),
|
||||
NETHERITE(6, 8, 3, 0.1, Material.NETHERITE_INGOT, 3, "netherite", 592),
|
||||
;
|
||||
|
||||
private final int tierID;
|
||||
@ -92,7 +93,7 @@ public enum ArmorTier {
|
||||
}
|
||||
// Overwrite the index for diamond-tier armor value.
|
||||
// This value is the same as netherite's tier. However, with the introduction of the NETHERITE armor tier,
|
||||
// a new system was introduced that doesn't rely on the armor value for determining the armortier.
|
||||
// a new system was introduced that doesn't rely on the armor value for determining the armor-tier.
|
||||
// Therefore, when using the old backup system, it is always going to be the diamond tier instead.
|
||||
// While no new elytras can be created using the old system, some may still
|
||||
// be around from when it was still used.
|
||||
@ -102,4 +103,5 @@ public enum ArmorTier {
|
||||
public static int getDefaultRepairCount(ArmorTier armorTier) {
|
||||
return armorTier.defaultRepairCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,9 +16,11 @@ import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ConfigLoader {
|
||||
|
||||
private static final String HEADER =
|
||||
"Config file for ArmoredElytra. Don't forget to make a backup before making changes!";
|
||||
|
||||
@ -36,12 +38,12 @@ public class ConfigLoader {
|
||||
private boolean noFlightDurability;
|
||||
private boolean useTierDurability;
|
||||
private boolean dropNetheriteAsChestplate;
|
||||
private LinkedHashSet<Enchantment> allowedEnchantments;
|
||||
private Set<Enchantment> allowedEnchantments;
|
||||
private List<List<Enchantment>> mutuallyExclusiveEnchantments;
|
||||
private boolean craftingInSmithingTable;
|
||||
private boolean allowUpgradeToNetherite;
|
||||
private boolean bypassWearPerm;
|
||||
private boolean bypassCraftPerm;
|
||||
private boolean bypassWearPermissions;
|
||||
private boolean bypassCraftPermissions;
|
||||
private boolean allowRenaming;
|
||||
private boolean allowAddingEnchantments;
|
||||
|
||||
@ -53,112 +55,95 @@ public class ConfigLoader {
|
||||
// Read the current config, the make a new one based on the old one or default values, whichever is applicable.
|
||||
private void makeConfig() {
|
||||
// All the comments for the various config options.
|
||||
String[] unbreakableComment =
|
||||
{
|
||||
"Setting this to true will cause armored elytras to be unbreakable.",
|
||||
"Changing this to false will NOT make unbreakable elytras breakable again!"
|
||||
};
|
||||
String[] flyDurabilityComment =
|
||||
{
|
||||
"Setting this to true will cause armored elytras to not lose any durability while flying.",
|
||||
"This is not a permanent option and will affect ALL elytras."
|
||||
};
|
||||
String[] useTierDurabilityComment =
|
||||
{
|
||||
"Use the maximum durability of the armor tier of armored elytras.",
|
||||
"For example, when this is true, a diamond armored elytra would have a durability of 528.",
|
||||
"When this is false, all armored elytras have the maximum durability of a regular elytra."
|
||||
};
|
||||
String[] repairComment =
|
||||
{
|
||||
"Amount of items it takes to fully repair an armored elytra",
|
||||
"Repair cost for every tier of armored elytra in number of items to repair 100%.",
|
||||
"Note that this value cannot be less than 1."
|
||||
};
|
||||
String[] enchantmentsComment =
|
||||
{
|
||||
"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 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",
|
||||
"This means that they won't burn in lava etc.",
|
||||
"When you pick them up, they will turn into Netherite Armored Elytras again."
|
||||
};
|
||||
String[] updateComment =
|
||||
{
|
||||
"Allow this plugin to check for updates on startup. It will not download new versions on its own!"
|
||||
};
|
||||
String[] bStatsComment =
|
||||
{
|
||||
"Allow this plugin to send (anonymous) stats using bStats. Please consider keeping it enabled.",
|
||||
"It has a negligible impact on performance and more users on stats keeps me more motivated " +
|
||||
"to support this plugin!"
|
||||
};
|
||||
String[] debugComment =
|
||||
{
|
||||
"Print debug messages to console. You will most likely never need this."
|
||||
};
|
||||
String[] uninstallComment =
|
||||
{
|
||||
"Setting this to true will disable this plugin and remove any armored elytras it can find.",
|
||||
"It will check player's inventories and their end chest upon login and any regular" +
|
||||
" chest when it is opened.",
|
||||
"This means it will take a while for all armored elytras to be removed from your server, " +
|
||||
"but it doesn't take up ",
|
||||
"a lot of resources, so you can just leave the plugin enabled and ignore it.",
|
||||
"Please do not forget to MAKE A BACKUP before enabling this option!"
|
||||
};
|
||||
String[] languageFileComment =
|
||||
{
|
||||
"Specify a language file to be used."
|
||||
};
|
||||
String[] permissionsComment =
|
||||
{
|
||||
"Globally bypass permissions for wearing and/or crafting armored elytras.",
|
||||
"Useful if permissions are unavailable."
|
||||
};
|
||||
String[] craftingInSmithingTableComment =
|
||||
{
|
||||
"When enabled, armored elytra creation in anvils is disabled. ",
|
||||
"Instead, you will have to craft them in a smithy. Enchanting/repairing them still works via the anvil."
|
||||
};
|
||||
String[] allowUpgradeToNetheriteComment =
|
||||
{
|
||||
"Whether or not to allow upgrading diamond armored elytras to netherite ones is possible.",
|
||||
"When allowed, you can combine a diamond one with a netherite ingot in a smithing table",
|
||||
"and you'll receive a netherite one."
|
||||
};
|
||||
String[] allowRenamingComment =
|
||||
{
|
||||
"Whether or not to allow renaming of armored elytras in anvils."
|
||||
};
|
||||
String[] allowAddingEnchantmentsComment =
|
||||
{
|
||||
"Allow new enchantments to be added to armored elytras.",
|
||||
"When false, no enchantments can be added to armored elytras, even those on the allowed list.",
|
||||
"When true, only enchantments from the allowed list can be added."
|
||||
};
|
||||
String[] unbreakableComment = {
|
||||
"Setting this to true will cause armored elytras to be unbreakable.",
|
||||
"Changing this to false will NOT make unbreakable elytras breakable again!"
|
||||
};
|
||||
String[] flyDurabilityComment = {
|
||||
"Setting this to true will cause armored elytras to not lose any durability while flying.",
|
||||
"This is not a permanent option and will affect ALL elytras."
|
||||
};
|
||||
String[] useTierDurabilityComment = {
|
||||
"Use the maximum durability of the armor tier of armored elytras.",
|
||||
"For example, when this is true, a diamond armored elytra would have a durability of 528.",
|
||||
"When this is false, all armored elytras have the maximum durability of a regular elytra."
|
||||
};
|
||||
String[] repairComment = {
|
||||
"Amount of items it takes to fully repair an armored elytra",
|
||||
"Repair cost for every tier of armored elytra in number of items to repair 100%.",
|
||||
"Note that this value cannot be less than 1."
|
||||
};
|
||||
String[] enchantmentsComment = {
|
||||
"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 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",
|
||||
"This means that they won't burn in lava etc.",
|
||||
"When you pick them up, they will turn into Netherite Armored Elytras again."
|
||||
};
|
||||
String[] updateComment = {
|
||||
"Allow this plugin to check for updates on startup. It will not download new versions on its own!"
|
||||
};
|
||||
String[] bStatsComment = {
|
||||
"Allow this plugin to send (anonymous) stats using bStats. Please consider keeping it enabled.",
|
||||
"It has a negligible impact on performance and more users on stats keeps me more motivated " +
|
||||
"to support this plugin!"
|
||||
};
|
||||
String[] debugComment = {
|
||||
"Print debug messages to console. You will most likely never need this."
|
||||
};
|
||||
String[] uninstallComment = {
|
||||
"Setting this to true will disable this plugin and remove any armored elytras it can find.",
|
||||
"It will check player's inventories and their end chest upon login and any regular" +
|
||||
" chest when it is opened.",
|
||||
"This means it will take a while for all armored elytras to be removed from your server, " +
|
||||
"but it doesn't take up ",
|
||||
"a lot of resources, so you can just leave the plugin enabled and ignore it.",
|
||||
"Please do not forget to MAKE A BACKUP before enabling this option!"
|
||||
};
|
||||
String[] languageFileComment = {
|
||||
"Specify a language file to be used."
|
||||
};
|
||||
String[] permissionsComment = {
|
||||
"Globally bypass permissions for wearing and/or crafting armored elytras.",
|
||||
"Useful if permissions are unavailable."
|
||||
};
|
||||
String[] craftingInSmithingTableComment = {
|
||||
"When enabled, armored elytra creation in anvils is disabled. ",
|
||||
"Instead, you will have to craft them in a smithy. Enchanting/repairing them still works via the anvil."
|
||||
};
|
||||
String[] allowUpgradeToNetheriteComment = {
|
||||
"Whether or not to allow upgrading diamond armored elytras to netherite ones is possible.",
|
||||
"When allowed, you can combine a diamond one with a netherite ingot in a smithing table",
|
||||
"and you'll receive a netherite one."
|
||||
};
|
||||
String[] allowRenamingComment = {
|
||||
"Whether or not to allow renaming of armored elytras in anvils."
|
||||
};
|
||||
String[] allowAddingEnchantmentsComment = {
|
||||
"Allow new enchantments to be added to armored elytras.",
|
||||
"When false, no enchantments can be added to armored elytras, even those on the allowed list.",
|
||||
"When true, only enchantments from the allowed list can be added."
|
||||
};
|
||||
|
||||
// Set default list of allowed enchantments.
|
||||
List<String> defaultAllowedEnchantments = new ArrayList<>(
|
||||
@ -177,21 +162,20 @@ public class ConfigLoader {
|
||||
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
|
||||
|
||||
unbreakable = addNewConfigOption(config, "unbreakable", false, unbreakableComment);
|
||||
noFlightDurability = addNewConfigOption(config, "noFlightDurability", false, flyDurabilityComment);
|
||||
useTierDurability = addNewConfigOption(config, "useTierDurability", true, useTierDurabilityComment);
|
||||
|
||||
final ArmorTier[] armorTiers = ArmorTier.values();
|
||||
for (int idx = 1; idx < armorTiers.length; ++idx) {
|
||||
final ArmorTier armorTier = armorTiers[idx];
|
||||
for (int armorTierId = 1; armorTierId < armorTiers.length; ++armorTierId) {
|
||||
final ArmorTier armorTier = armorTiers[armorTierId];
|
||||
|
||||
// Only the first one should have the comment.
|
||||
final @Nullable String[] comment = idx == 1 ? repairComment : null;
|
||||
final @Nullable String[] comment = armorTierId == 1 ? repairComment : null;
|
||||
final String name = Util.snakeToCamelCase(ArmorTier.getRepairItem(armorTier).name());
|
||||
final int defaultRepairCount = ArmorTier.getDefaultRepairCount(armorTier);
|
||||
|
||||
repairCounts[idx] = addNewConfigOption(config, name, defaultRepairCount, comment);
|
||||
repairCounts[armorTierId] = addNewConfigOption(config, name, defaultRepairCount, comment);
|
||||
}
|
||||
|
||||
final int armorTierCount = ArmorTier.values().length;
|
||||
@ -227,8 +211,9 @@ public class ConfigLoader {
|
||||
enableDebug = addNewConfigOption(config, "enableDebug", false, debugComment);
|
||||
uninstallMode = addNewConfigOption(config, "uninstallMode", false, uninstallComment);
|
||||
languageFile = addNewConfigOption(config, "languageFile", "en_US", languageFileComment);
|
||||
bypassWearPerm = addNewConfigOption(config, "bypassWearPermissions", true, permissionsComment);
|
||||
bypassCraftPerm = addNewConfigOption(config, "bypassCraftPermissions", true, null);
|
||||
bypassWearPermissions = addNewConfigOption(config, "bypassWearPermissions", true, permissionsComment);
|
||||
bypassCraftPermissions = addNewConfigOption(config, "bypassCraftPermissions", true, null);
|
||||
|
||||
|
||||
writeConfig();
|
||||
}
|
||||
@ -359,7 +344,7 @@ public class ConfigLoader {
|
||||
return dropNetheriteAsChestplate;
|
||||
}
|
||||
|
||||
public LinkedHashSet<Enchantment> allowedEnchantments() {
|
||||
public Set<Enchantment> allowedEnchantments() {
|
||||
return allowedEnchantments;
|
||||
}
|
||||
|
||||
@ -368,14 +353,15 @@ public class ConfigLoader {
|
||||
}
|
||||
|
||||
public boolean bypassWearPerm() {
|
||||
return bypassWearPerm;
|
||||
return bypassWearPermissions;
|
||||
}
|
||||
|
||||
public boolean bypassCraftPerm() {
|
||||
return bypassCraftPerm;
|
||||
return bypassCraftPermissions;
|
||||
}
|
||||
|
||||
public boolean useTierDurability() {
|
||||
return useTierDurability;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,21 +36,20 @@ public class Util {
|
||||
}
|
||||
|
||||
// Get the armor tier from a chest plate.
|
||||
public static ArmorTier armorToTier(Material mat) {
|
||||
ArmorTier ret = ArmorTier.NONE;
|
||||
XMaterial xmat = XMaterial.matchXMaterial(mat);
|
||||
public static ArmorTier armorToTier(Material material) {
|
||||
ArmorTier armorTier = ArmorTier.NONE;
|
||||
|
||||
switch (xmat) {
|
||||
case LEATHER_CHESTPLATE -> ret = ArmorTier.LEATHER;
|
||||
case GOLDEN_CHESTPLATE -> ret = ArmorTier.GOLD;
|
||||
case CHAINMAIL_CHESTPLATE -> ret = ArmorTier.CHAIN;
|
||||
case IRON_CHESTPLATE -> ret = ArmorTier.IRON;
|
||||
case DIAMOND_CHESTPLATE -> ret = ArmorTier.DIAMOND;
|
||||
case NETHERITE_CHESTPLATE -> ret = ArmorTier.NETHERITE;
|
||||
switch (material) {
|
||||
case LEATHER_CHESTPLATE -> armorTier = ArmorTier.LEATHER;
|
||||
case GOLDEN_CHESTPLATE -> armorTier = ArmorTier.GOLD;
|
||||
case CHAINMAIL_CHESTPLATE -> armorTier = ArmorTier.CHAIN;
|
||||
case IRON_CHESTPLATE -> armorTier = ArmorTier.IRON;
|
||||
case DIAMOND_CHESTPLATE -> armorTier = ArmorTier.DIAMOND;
|
||||
case NETHERITE_CHESTPLATE -> armorTier = ArmorTier.NETHERITE;
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return armorTier;
|
||||
}
|
||||
|
||||
public static boolean isChestPlate(ItemStack itemStack) {
|
||||
@ -58,17 +57,10 @@ public class Util {
|
||||
}
|
||||
|
||||
// Check if mat is a chest plate.
|
||||
public static boolean isChestPlate(Material mat) {
|
||||
try {
|
||||
XMaterial xmat = XMaterial.matchXMaterial(mat);
|
||||
|
||||
return xmat == XMaterial.LEATHER_CHESTPLATE || xmat == XMaterial.GOLDEN_CHESTPLATE ||
|
||||
xmat == XMaterial.CHAINMAIL_CHESTPLATE || xmat == XMaterial.IRON_CHESTPLATE ||
|
||||
xmat == XMaterial.DIAMOND_CHESTPLATE || xmat == XMaterial.NETHERITE_CHESTPLATE;
|
||||
} catch (IllegalArgumentException e) {
|
||||
// No need to handle this, this is just XMaterial complaining the material doesn't exist.
|
||||
return false;
|
||||
}
|
||||
public static boolean isChestPlate(Material material) {
|
||||
return material == Material.LEATHER_CHESTPLATE || material == Material.GOLDEN_CHESTPLATE ||
|
||||
material == Material.CHAINMAIL_CHESTPLATE || material == Material.IRON_CHESTPLATE ||
|
||||
material == Material.DIAMOND_CHESTPLATE || material == Material.NETHERITE_CHESTPLATE;
|
||||
}
|
||||
|
||||
public static String snakeToCamelCase(String input) {
|
||||
@ -147,4 +139,23 @@ public class Util {
|
||||
public static int between(int val, int min, int max) {
|
||||
return Math.max(min, Math.min(max, val));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the correct texture id from the given armor tier
|
||||
*
|
||||
* @param armorTier <p>The armor tier to get texture for</p>
|
||||
* @return <p>The texture to be used for the given armor tier</p>
|
||||
*/
|
||||
public static Integer getTextureIdFromArmorTier(ArmorTier armorTier) {
|
||||
return switch (armorTier) {
|
||||
case NONE -> null;
|
||||
case LEATHER -> 471301;
|
||||
case GOLD -> 471302;
|
||||
case CHAIN -> 471303;
|
||||
case IRON -> 471304;
|
||||
case DIAMOND -> 471305;
|
||||
case NETHERITE -> 471306;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,970 +0,0 @@
|
||||
package nl.pim16aap2.armoredElytra.util;
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Hex_27
|
||||
* Copyright (c) 2020 Crypto Morin
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import com.google.common.base.Enums;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <b>XMaterial</b> - Data Values/Pre-flattening<br>
|
||||
* 1.13 and above as priority.
|
||||
* <p>
|
||||
* This class is mainly designed to support ItemStacks. If you want to use it on blocks you'll have to use <a
|
||||
* href="https://github.com/CryptoMorin/XSeries/blob/master/XBlock.java">XBlock</a>
|
||||
* <p>
|
||||
* Pre-flattening: https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening Materials:
|
||||
* https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html Materials (1.12):
|
||||
* https://helpch.at/docs/1.12.2/index.html?org/bukkit/Material.html Material IDs:
|
||||
* https://minecraft-ids.grahamedgecombe.com/ Material Source Code:
|
||||
* <p>
|
||||
* https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/Material.java XMaterial
|
||||
* v1: https://www.spigotmc.org/threads/329630/
|
||||
*
|
||||
* @author Crypto Morin
|
||||
* @version 5.0.0
|
||||
* @see Material
|
||||
* @see ItemStack
|
||||
*/
|
||||
public enum XMaterial {
|
||||
CHAINMAIL_CHESTPLATE,
|
||||
DIAMOND_CHESTPLATE,
|
||||
GOLDEN_CHESTPLATE("GOLD_CHESTPLATE"),
|
||||
IRON_CHESTPLATE,
|
||||
LEATHER_CHESTPLATE,
|
||||
NETHERITE_CHESTPLATE("1.16"),
|
||||
PHANTOM_MEMBRANE("1.13"),
|
||||
AIR,
|
||||
NETHERITE_INGOT("1.16"),
|
||||
;
|
||||
|
||||
|
||||
/**
|
||||
* An immutable cached set of {@link XMaterial#values()} to avoid allocating memory for calling the method every
|
||||
* time.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public static final EnumSet<XMaterial> VALUES = EnumSet.allOf(XMaterial.class);
|
||||
/**
|
||||
* A set of material names that can be damaged.
|
||||
* <p>
|
||||
* Most of the names are not complete as this list is intended to be checked with {@link String#contains} for memory
|
||||
* usage.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static final ImmutableSet<String> DAMAGEABLE = ImmutableSet.of(
|
||||
"HELMET", "CHESTPLATE", "LEGGINGS", "BOOTS",
|
||||
"SWORD", "AXE", "PICKAXE", "SHOVEL", "HOE",
|
||||
"ELYTRA", "TRIDENT", "HORSE_ARMOR", "BARDING",
|
||||
"SHEARS", "FLINT_AND_STEEL", "BOW", "FISHING_ROD",
|
||||
"CARROT_ON_A_STICK", "CARROT_STICK", "SPADE", "SHIELD"
|
||||
);
|
||||
|
||||
/*
|
||||
* A set of all the legacy names without duplicates.
|
||||
* <p>
|
||||
* It'll help to free up a lot of memory if it's not used.
|
||||
* Add it back if you need it.
|
||||
*
|
||||
* @see #containsLegacy(String)
|
||||
* @since 2.2.0
|
||||
*
|
||||
private static final ImmutableSet<String> LEGACY_VALUES = VALUES.stream().map(XMaterial::getLegacy)
|
||||
.flatMap(Arrays::stream)
|
||||
.filter(m -> m.charAt(1) == '.')
|
||||
.collect(Collectors.collectingAndThen(Collectors.toSet(), ImmutableSet::copyOf));
|
||||
*/
|
||||
|
||||
/**
|
||||
* Guava (Google Core Libraries for Java)'s cache for performance and timed caches. For strings that match a certain
|
||||
* XMaterial. Mostly cached for configs.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static final Cache<String, XMaterial> NAME_CACHE
|
||||
= CacheBuilder.newBuilder()
|
||||
.softValues()
|
||||
.expireAfterAccess(15, TimeUnit.MINUTES)
|
||||
.build();
|
||||
/**
|
||||
* Guava (Google Core Libraries for Java)'s cache for performance and timed caches. For XMaterials that are already
|
||||
* parsed once.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private static final Cache<XMaterial, Optional<Material>> PARSED_CACHE
|
||||
= CacheBuilder.newBuilder()
|
||||
.softValues()
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.concurrencyLevel(Runtime.getRuntime().availableProcessors())
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Pre-compiled RegEx pattern. Include both replacements to avoid recreating string multiple times with multiple
|
||||
* RegEx checks.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private static final Pattern FORMAT_PATTERN = Pattern.compile("\\W+");
|
||||
/**
|
||||
* The current version of the server in the a form of a major version.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private static final int VERSION = Integer.parseInt(getMajorVersion(Bukkit.getVersion()).substring(2));
|
||||
/**
|
||||
* Cached result if the server version is after the v1.13 flattening update. Please don't mistake this with
|
||||
* flat-chested people. It happened.
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private static final boolean ISFLAT = supports(13);
|
||||
/**
|
||||
* The data value of this material https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening
|
||||
*
|
||||
* @see #getData()
|
||||
*/
|
||||
private final byte data;
|
||||
/**
|
||||
* A list of material names that was being used for older verions.
|
||||
*
|
||||
* @see #getLegacy()
|
||||
*/
|
||||
private final String[] legacy;
|
||||
|
||||
XMaterial(int data, String... legacy) {
|
||||
this.data = (byte) data;
|
||||
this.legacy = legacy;
|
||||
}
|
||||
|
||||
XMaterial() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
XMaterial(String... legacy) {
|
||||
this(0, legacy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the version is 1.13 Aquatic Update or higher. An invocation of this method yields the cached result
|
||||
* from the expression:
|
||||
* <p>
|
||||
* <blockquote>
|
||||
* {@link #supports(int) 13}}
|
||||
* </blockquote>
|
||||
*
|
||||
* @return true if 1.13 or higher.
|
||||
* @see #getVersion()
|
||||
* @see #supports(int)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static boolean isNewVersion() {
|
||||
return ISFLAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is just an extra method that method that can be used for many cases. It can be used in {@link
|
||||
* org.bukkit.event.player.PlayerInteractEvent} or when accessing {@link org.bukkit.entity.Player#getMainHand()}, or
|
||||
* other compatibility related methods.
|
||||
* <p>
|
||||
* An invocation of this method yields exactly the same result as the expression:
|
||||
* <p>
|
||||
* <blockquote>
|
||||
* {@link #getVersion()} == 1.8
|
||||
* </blockquote>
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public static boolean isOneEight() {
|
||||
return !supports(9);
|
||||
}
|
||||
|
||||
/**
|
||||
* The current version of the server.
|
||||
*
|
||||
* @return the current server version or 0.0 if unknown.
|
||||
* @see #isNewVersion()
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public static double getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* When using newer versions of Minecraft ({@link #isNewVersion()}), helps to find the old material name with its
|
||||
* data value using a cached search for optimization.
|
||||
*
|
||||
* @see #matchDefinedXMaterial(String, byte)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
private static XMaterial requestOldXMaterial(@Nonnull String name, byte data) {
|
||||
String holder = name + data;
|
||||
XMaterial cache = NAME_CACHE.getIfPresent(holder);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
|
||||
for (XMaterial material : VALUES) {
|
||||
// Not using material.name().equals(name) check is intended.
|
||||
if ((data == -1 || data == material.data) && material.anyMatchLegacy(name)) {
|
||||
NAME_CACHE.put(holder, material);
|
||||
return material;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if XMaterial enum contains a material with the given name.
|
||||
* <p>
|
||||
* You should use {@link #matchXMaterial(String)} instead if you're going to get the XMaterial object after checking
|
||||
* if it's available in the list by doing a simple {@link Optional#isPresent()} check. This is just to avoid
|
||||
* multiple loops for maximum performance.
|
||||
*
|
||||
* @param name name of the material.
|
||||
* @return true if XMaterial enum has this material.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static boolean contains(@Nonnull String name) {
|
||||
Validate.notEmpty(name, "Cannot check for null or empty material name");
|
||||
name = format(name);
|
||||
|
||||
for (XMaterial materials : VALUES)
|
||||
if (materials.name().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given material name as an XMaterial with unspecified data value.
|
||||
*
|
||||
* @see #matchXMaterialWithData(String)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public static Optional<XMaterial> matchXMaterial(@Nonnull String name) {
|
||||
Validate.notEmpty(name, "Cannot match a material with null or empty material name");
|
||||
Optional<XMaterial> oldMatch = matchXMaterialWithData(name);
|
||||
if (oldMatch.isPresent()) {
|
||||
return oldMatch;
|
||||
}
|
||||
return matchDefinedXMaterial(format(name), (byte) -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses material name and data value from the specified string. The seperators are: <b>, or :</b> Spaces are
|
||||
* allowed. Mostly used when getting materials from config for old school minecrafters.
|
||||
* <p>
|
||||
* <b>Examples</b>
|
||||
* <p><pre>
|
||||
* {@code INK_SACK:1 -> RED_DYE}
|
||||
* {@code WOOL, 14 -> RED_WOOL}
|
||||
* </pre>
|
||||
*
|
||||
* @param name the material string that consists of the material name, data and separator character.
|
||||
* @return the parsed XMaterial.
|
||||
* @see #matchXMaterial(String)
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private static Optional<XMaterial> matchXMaterialWithData(String name) {
|
||||
for (char separator : new char[]{',', ':'}) {
|
||||
int index = name.indexOf(separator);
|
||||
if (index == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String mat = format(name.substring(0, index));
|
||||
byte data = Byte.parseByte(StringUtils.deleteWhitespace(name.substring(index + 1)));
|
||||
return matchDefinedXMaterial(mat, data);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given material as an XMaterial.
|
||||
*
|
||||
* @throws IllegalArgumentException may be thrown as an unexpected exception.
|
||||
* @see #matchDefinedXMaterial(String, byte)
|
||||
* @see #matchXMaterial(ItemStack)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public static XMaterial matchXMaterial(@Nonnull Material material) {
|
||||
Objects.requireNonNull(material, "Cannot match null material");
|
||||
return matchDefinedXMaterial(material.name(), (byte) -1)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unsupported Material With No Bytes: " + material.name()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given item as an XMaterial using its material and data value (durability).
|
||||
*
|
||||
* @param item the ItemStack to match.
|
||||
* @return an XMaterial if matched any.
|
||||
* @throws IllegalArgumentException may be thrown as an unexpected exception.
|
||||
* @see #matchDefinedXMaterial(String, byte)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings("deprecation")
|
||||
public static XMaterial matchXMaterial(@Nonnull ItemStack item) {
|
||||
Objects.requireNonNull(item, "Cannot match null ItemStack");
|
||||
String material = item.getType().name();
|
||||
byte data = (byte) (ISFLAT || isDamageable(material) ? 0 : item.getDurability());
|
||||
|
||||
return matchDefinedXMaterial(material, data)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Unsupported Material: " + material + " (" + data + ')'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given material name and data value as an XMaterial. All the values passed to this method will not be
|
||||
* null or empty and are formatted correctly.
|
||||
*
|
||||
* @param name the formatted name of the material.
|
||||
* @param data the data value of the material.
|
||||
* @return an XMaterial (with the same data value if specified)
|
||||
* @see #matchXMaterial(Material)
|
||||
* @see #matchXMaterial(int, byte)
|
||||
* @see #matchXMaterial(ItemStack)
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
private static Optional<XMaterial> matchDefinedXMaterial(@Nonnull String name, byte data) {
|
||||
boolean duplicated = isDuplicated(name);
|
||||
|
||||
// Do basic number and boolean checks before accessing more complex enum stuff.
|
||||
// Maybe we can simplify (ISFLAT || !duplicated) with the (!ISFLAT && duplicated)
|
||||
// under it to save a few nanoseconds?
|
||||
if (data <= 0 && !duplicated) {
|
||||
// Apparently the transform method is more efficient than toJavaUtil()
|
||||
// toJavaUtil isn't even supported in older versions.
|
||||
Optional<XMaterial> xMat =
|
||||
Enums.getIfPresent(XMaterial.class, name).transform(Optional::of).or(Optional.empty());
|
||||
|
||||
if (xMat.isPresent()) {
|
||||
return xMat;
|
||||
}
|
||||
}
|
||||
|
||||
// XMaterial Paradox (Duplication Check)
|
||||
// I've concluded that this is just an infinite loop that keeps
|
||||
// going around the Singular Form and the Plural Form materials. A waste of brain cells and a waste of time.
|
||||
// This solution works just fine anyway.
|
||||
XMaterial xMat = requestOldXMaterial(name, data);
|
||||
if (xMat == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (!ISFLAT && duplicated && xMat.name().charAt(xMat.name().length() - 1) == 'S') {
|
||||
// A solution for XMaterial Paradox.
|
||||
// Manually parses the duplicated materials to find the exact material based on the server version.
|
||||
// If ends with "S" -> Plural Form Material
|
||||
return Enums.getIfPresent(XMaterial.class, name).transform(Optional::of).or(Optional.empty());
|
||||
}
|
||||
return Optional.ofNullable(xMat);
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>XMaterial Paradox (Duplication Check)</b>
|
||||
* Checks if the material has any duplicates.
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* <p>{@code MELON, CARROT, POTATO, BEETROOT -> true}
|
||||
*
|
||||
* @param name the name of the material to check.
|
||||
* @return true if there's a duplicated material for this material, otherwise false.
|
||||
* @see #isDuplicated()
|
||||
* @since 2.0.0
|
||||
*/
|
||||
private static boolean isDuplicated(@Nonnull String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the XMaterial based on the material's ID (Magic Value) and data value.<br> You should avoid using this for
|
||||
* performance issues.
|
||||
*
|
||||
* @param id the ID (Magic value) of the material.
|
||||
* @param data the data value of the material.
|
||||
* @return a parsed XMaterial with the same ID and data value.
|
||||
* @see #matchXMaterial(ItemStack)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public static Optional<XMaterial> matchXMaterial(int id, byte data) {
|
||||
if (id < 0 || data < 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// Looping through Material.values() will take longer.
|
||||
for (XMaterial materials : VALUES)
|
||||
if (materials.data == data && materials.getId() == id) {
|
||||
return Optional.of(materials);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to build the string like an enum name. Removes all the spaces, numbers and extra non-English characters.
|
||||
* Also removes some config/in-game based strings.
|
||||
*
|
||||
* @param name the material name to modify.
|
||||
* @return a Material enum name.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
private static String format(@Nonnull String name) {
|
||||
return FORMAT_PATTERN.matcher(
|
||||
name.trim().replace('-', '_').replace(' ', '_')).replaceAll("").toUpperCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified version is the same version or higher than the current server version.
|
||||
*
|
||||
* @param version the major version to be checked. "1." is ignored. E.g. 1.12 = 12 | 1.9 = 9
|
||||
* @return true of the version is equal or higher than the current version.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public static boolean supports(int version) {
|
||||
return VERSION >= version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the enum names to a more friendly and readable string.
|
||||
*
|
||||
* @return a formatted string.
|
||||
* @see #toWord(String)
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@Nonnull
|
||||
public static String toWord(@Nonnull Material material) {
|
||||
Objects.requireNonNull(material, "Cannot translate a null material to a word");
|
||||
return toWord(material.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an enum name to a normal word. Normal names have underlines removed and each word capitalized.
|
||||
* <p>
|
||||
* <b>Examples:</b>
|
||||
* <pre>
|
||||
* EMERALD -> Emerald
|
||||
* EMERALD_BLOCK -> Emerald Block
|
||||
* ENCHANTED_GOLDEN_APPLE -> Enchanted Golden Apple
|
||||
* </pre>
|
||||
*
|
||||
* @param name the name of the enum.
|
||||
* @return a cleaned more readable enum name.
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@Nonnull
|
||||
private static String toWord(@Nonnull String name) {
|
||||
return WordUtils.capitalize(name.replace('_', ' ').toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exact major version (..., 1.9, 1.10, ..., 1.14)
|
||||
*
|
||||
* @param version Supports {@link Bukkit#getVersion()}, {@link Bukkit#getBukkitVersion()} and normal formats such as
|
||||
* "1.14"
|
||||
* @return the exact major version.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public static String getMajorVersion(@Nonnull String version) {
|
||||
Validate.notEmpty(version, "Cannot get major Minecraft version from null or empty string");
|
||||
|
||||
// getVersion()
|
||||
int index = version.lastIndexOf("MC:");
|
||||
if (index != -1) {
|
||||
version = version.substring(index + 4, version.length() - 1);
|
||||
} else if (version.endsWith("SNAPSHOT")) {
|
||||
// getBukkitVersion()
|
||||
index = version.indexOf('-');
|
||||
version = version.substring(0, index);
|
||||
}
|
||||
|
||||
// 1.13.2, 1.14.4, etc...
|
||||
int lastDot = version.lastIndexOf('.');
|
||||
if (version.indexOf('.') != lastDot) {
|
||||
version = version.substring(0, lastDot);
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the material can be damaged by using it. Names going through this method are not formatted.
|
||||
*
|
||||
* @param name the name of the material.
|
||||
* @return true of the material can be damaged.
|
||||
* @see #isDamageable()
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static boolean isDamageable(@Nonnull String name) {
|
||||
Objects.requireNonNull(name, "Material name cannot be null");
|
||||
for (String damageable : DAMAGEABLE)
|
||||
if (name.contains(damageable)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the list of given material names matches the given base material. Mostly used for configs.
|
||||
* <p>
|
||||
* Supports {@link String#contains} {@code CONTAINS:NAME} and Regular Expression {@code REGEX:PATTERN} formats.
|
||||
* <p>
|
||||
* <b>Example:</b>
|
||||
* <blockquote><pre>
|
||||
* XMaterial material = {@link #matchXMaterial(ItemStack)};
|
||||
* if (material.isOneOf(plugin.getConfig().getStringList("disabled-items")) return;
|
||||
* </pre></blockquote>
|
||||
* <br>
|
||||
* <b>{@code CONTAINS} Examples:</b>
|
||||
* <pre>
|
||||
* {@code "CONTAINS:CHEST" -> CHEST, ENDERCHEST, TRAPPED_CHEST -> true}
|
||||
* {@code "cOnTaINS:dYe" -> GREEN_DYE, YELLOW_DYE, BLUE_DYE, INK_SACK -> true}
|
||||
* </pre>
|
||||
* <p>
|
||||
* <b>{@code REGEX} Examples</b>
|
||||
* <pre>
|
||||
* {@code "REGEX:^.+_.+_.+$" -> Every Material with 3 underlines or more: SHULKER_SPAWN_EGG,
|
||||
* SILVERFISH_SPAWN_EGG, SKELETON_HORSE_SPAWN_EGG}
|
||||
* {@code "REGEX:^.{1,3}$" -> Material names that have 3 letters only: BED, MAP, AIR}
|
||||
* </pre>
|
||||
* <p>
|
||||
* The reason that there are tags for {@code CONTAINS} and {@code REGEX} is for the performance. Please avoid using
|
||||
* the {@code REGEX} tag if you can use the {@code CONTAINS} tag. It'll have a huge impact on performance. Please
|
||||
* avoid using {@code (capturing groups)} there's no use for them in this case. If you want to use groups, use
|
||||
* {@code (?: non-capturing groups)}. It's faster.
|
||||
* <p>
|
||||
* You can make a cache for pre-compiled RegEx patterns from your config. It's better, but not much faster since
|
||||
* these patterns are not that complex.
|
||||
* <p>
|
||||
* Want to learn RegEx? You can mess around in <a href="https://regexr.com/">RegExr</a> website.
|
||||
*
|
||||
* @param material the base material to match other materials with.
|
||||
* @param materials the material names to check base material on.
|
||||
* @return true if one of the given material names is similar to the base material.
|
||||
* @since 3.1.1
|
||||
*/
|
||||
public static boolean isOneOf(@Nonnull Material material, @Nullable List<String> materials) {
|
||||
if (materials == null || materials.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Objects.requireNonNull(material, "Cannot match materials with a null material");
|
||||
String name = material.name();
|
||||
|
||||
for (String comp : materials) {
|
||||
comp = comp.toUpperCase();
|
||||
if (comp.startsWith("CONTAINS:")) {
|
||||
comp = format(comp.substring(9));
|
||||
if (name.contains(comp)) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (comp.startsWith("REGEX:")) {
|
||||
comp = comp.substring(6);
|
||||
if (name.matches(comp)) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Direct Object Equals
|
||||
Optional<XMaterial> mat = matchXMaterial(comp);
|
||||
if (mat.isPresent() && mat.get().parseMaterial() == material) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the version which this material was added in. If the material doesn't have a version it'll return 0;
|
||||
*
|
||||
* @return the Minecraft version which tihs material was added in.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public int getMaterialVersion() {
|
||||
if (this.legacy.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
String version = this.legacy[0];
|
||||
if (version.charAt(1) != '.') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Integer.parseInt(version.substring(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Material} (and data value on older versions) of an item. Damageable materials will not have their
|
||||
* durability changed.
|
||||
* <p>
|
||||
* Use {@link #parseItem()} instead when creating new ItemStacks.
|
||||
*
|
||||
* @param item the item to change its type.
|
||||
* @see #parseItem()
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
@SuppressWarnings("deprecation")
|
||||
public ItemStack setType(@Nonnull ItemStack item) {
|
||||
Objects.requireNonNull(item, "Cannot set material for null ItemStack");
|
||||
|
||||
item.setType(this.parseMaterial());
|
||||
if (!ISFLAT && !this.isDamageable()) {
|
||||
item.setDurability(this.data);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the list of given material names matches the given base material. Mostly used for configs.
|
||||
*
|
||||
* @param materials the material names to check base material on.
|
||||
* @return true if one of the given material names is similar to the base material.
|
||||
* @see #isOneOf(Material, List)
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public boolean isOneOf(@Nullable List<String> materials) {
|
||||
Material material = this.parseMaterial();
|
||||
if (material == null) {
|
||||
return false;
|
||||
}
|
||||
return isOneOf(material, materials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given string matches any of this material's legacy material names. All the values passed to this
|
||||
* method will not be null or empty and are formatted correctly.
|
||||
*
|
||||
* @param name the name to check
|
||||
* @return true if it's one of the legacy names.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
private boolean anyMatchLegacy(@Nonnull String name) {
|
||||
for (String legacy : this.legacy) {
|
||||
if (legacy.isEmpty())
|
||||
// Left-side suggestion list
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (name.equals(legacy)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* User-friendly readable name for this material In most cases you should be using {@link #name()} instead.
|
||||
*
|
||||
* @return string of this object.
|
||||
* @see #toWord(String)
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return toWord(this.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID (Magic value) of the material.
|
||||
*
|
||||
* @return the ID of the material or <b>-1</b> if it's a new block or the material is not supported.
|
||||
* @see #matchXMaterial(int, byte)
|
||||
* @since 2.2.0
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public int getId() {
|
||||
if (this.data != 0 || (this.legacy.length != 0 && Integer.parseInt(this.legacy[0].substring(2)) >= 13)) {
|
||||
return -1;
|
||||
}
|
||||
Material material = this.parseMaterial();
|
||||
return material == null ? -1 : material.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the material has any duplicates.
|
||||
*
|
||||
* @return true if there is a duplicated name for this material, otherwise false.
|
||||
* @see #isDuplicated(String)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public boolean isDuplicated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the material can be damaged by using it. Names going through this method are not formatted.
|
||||
*
|
||||
* @return true if the item can be damaged (have its durability changed), otherwise false.
|
||||
* @see #isDamageable(String)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public boolean isDamageable() {
|
||||
return isDamageable(this.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* The data value of this material
|
||||
* <a href="https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening">pre-flattening</a>.
|
||||
* <p>
|
||||
* Can be accessed with {@link ItemStack#getData()} then {@code MaterialData#getData()} or {@link
|
||||
* ItemStack#getDurability()} if not damageable.
|
||||
*
|
||||
* @return data of this material, or 0 if none.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public byte getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of materials names that was previously used by older versions. If the material was added in a new
|
||||
* version {@link #isNewVersion()}, then the first element will indicate which version the material was added in.
|
||||
*
|
||||
* @return a list of legacy material names and the first element as the version the material was added in if new.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public String[] getLegacy() {
|
||||
return legacy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an item from this XMaterial. Uses data values on older versions.
|
||||
*
|
||||
* @return an ItemStack with the same material (and data value if in older versions.)
|
||||
* @see #parseItem(boolean)
|
||||
* @see #setType(ItemStack)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public ItemStack parseItem() {
|
||||
return parseItem(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an item from this XMaterial. Uses data values on older versions.
|
||||
*
|
||||
* @param suggest if true {@link #parseMaterial(boolean)} true will be used.
|
||||
* @return an ItemStack with the same material (and data value if in older versions.)
|
||||
* @see #setType(ItemStack)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("deprecation")
|
||||
public ItemStack parseItem(boolean suggest) {
|
||||
Material material = this.parseMaterial(suggest);
|
||||
if (material == null) {
|
||||
return null;
|
||||
}
|
||||
return ISFLAT ? new ItemStack(material) : new ItemStack(material, 1, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the material of this XMaterial.
|
||||
*
|
||||
* @return the material related to this XMaterial based on the server version.
|
||||
* @see #parseMaterial(boolean)
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Nullable
|
||||
public Material parseMaterial() {
|
||||
return parseMaterial(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the material of this XMaterial and accepts suggestions.
|
||||
*
|
||||
* @param suggest use a suggested material (from older materials) if the material is added in a later version of
|
||||
* Minecraft.
|
||||
* @return the material related to this XMaterial based on the server version.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@SuppressWarnings("OptionalAssignedToNull")
|
||||
@Nullable
|
||||
public Material parseMaterial(boolean suggest) {
|
||||
Optional<Material> cache = PARSED_CACHE.getIfPresent(this);
|
||||
if (cache != null) {
|
||||
return cache.orElse(null);
|
||||
}
|
||||
Material mat;
|
||||
|
||||
if (!ISFLAT && this.isDuplicated()) {
|
||||
mat = requestOldMaterial(suggest);
|
||||
} else {
|
||||
mat = Material.getMaterial(this.name());
|
||||
if (mat == null) {
|
||||
mat = requestOldMaterial(suggest);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat != null) {
|
||||
PARSED_CACHE.put(this, Optional.ofNullable(mat));
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a material for older versions of Minecraft. Accepts suggestions if specified.
|
||||
*
|
||||
* @param suggest if true suggested materials will be considered for old versions.
|
||||
* @return a parsed material suitable for the current Minecraft version.
|
||||
* @see #parseMaterial(boolean)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nullable
|
||||
private Material requestOldMaterial(boolean suggest) {
|
||||
for (int i = this.legacy.length - 1; i >= 0; i--) {
|
||||
String legacy = this.legacy[i];
|
||||
|
||||
// Check if we've reached the end and the last string is our
|
||||
// material version.
|
||||
if (i == 0 && legacy.charAt(1) == '.') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// According to the suggestion list format, all the other names continuing
|
||||
// from here are considered as a "suggestion"
|
||||
// The empty string is an indicator for suggestion list on the left side.
|
||||
if (legacy.isEmpty()) {
|
||||
if (suggest) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Material material = Material.getMaterial(legacy);
|
||||
if (material != null) {
|
||||
return material;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an item has the same material (and data value on older versions).
|
||||
*
|
||||
* @param item item to check.
|
||||
* @return true if the material is the same as the item's material (and data value if on older versions), otherwise
|
||||
* false.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean isSimilar(@Nonnull ItemStack item) {
|
||||
Objects.requireNonNull(item, "Cannot compare with null ItemStack");
|
||||
if (item.getType() != this.parseMaterial()) {
|
||||
return false;
|
||||
}
|
||||
return ISFLAT || this.isDamageable() || item.getDurability() == this.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the suggested material names that can be used if the material is not supported in the current version.
|
||||
*
|
||||
* @return a list of suggested material names.
|
||||
* @see #parseMaterial(boolean)
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Nonnull
|
||||
public List<String> getSuggestions() {
|
||||
if (this.legacy.length == 0 || this.legacy[0].charAt(1) != '.') {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
for (String legacy : this.legacy) {
|
||||
if (legacy.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
suggestions.add(legacy);
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this material is supported in the current version. Suggested materials will be ignored.
|
||||
* <p>
|
||||
* Note that you should use {@link #parseMaterial()} and check if it's null if you're going to parse and use the
|
||||
* material later.
|
||||
*
|
||||
* @return true if the material exists in {@link Material} list.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public boolean isSupported() {
|
||||
int version = this.getMaterialVersion();
|
||||
if (version != 0) {
|
||||
return supports(version);
|
||||
}
|
||||
|
||||
Material material = Material.getMaterial(this.name());
|
||||
if (material != null) {
|
||||
return true;
|
||||
}
|
||||
return requestOldMaterial(false) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the material is newly added after the 1.13 Aquatic Update.
|
||||
*
|
||||
* @return true if the material was newly added, otherwise false.
|
||||
* @see #getMaterialVersion()
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public boolean isFromNewSystem() {
|
||||
return this.legacy.length != 0 && Integer.parseInt(this.legacy[0].substring(2)) > 13;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
package nl.pim16aap2.armoredElytra.util.messages;
|
||||
|
||||
/**
|
||||
* Represents supported variables in localizable messages.
|
||||
*
|
||||
* @author Pim
|
||||
*/
|
||||
public interface IMessageVariable {
|
||||
String VAR_TIER_NAME = "%ARMOR_TIER%";
|
||||
String VAR_TIER_NAME_SHORT = "%ARMOR_TIER_SHORT%";
|
||||
}
|
@ -5,7 +5,7 @@ package nl.pim16aap2.armoredElytra.util.messages;
|
||||
*
|
||||
* @author Pim
|
||||
*/
|
||||
public enum Message implements IMessageVariable {
|
||||
public enum Message implements MessageVariable {
|
||||
EMPTY(),
|
||||
|
||||
TIER_LEATHER(),
|
||||
@ -26,10 +26,10 @@ public enum Message implements IMessageVariable {
|
||||
MESSAGES_UNSUPPORTEDTIER(),
|
||||
|
||||
MESSAGES_REPAIRNEEDED(),
|
||||
MESSAGES_LORE(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
|
||||
MESSAGES_NOGIVEPERMISSION(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
|
||||
MESSAGES_USAGEDENIED(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
|
||||
MESSAGES_ELYTRARECEIVED(VAR_TIER_NAME, VAR_TIER_NAME_SHORT),
|
||||
MESSAGES_LORE(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
|
||||
MESSAGES_NOGIVEPERMISSION(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
|
||||
MESSAGES_USAGEDENIED(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
|
||||
MESSAGES_ELYTRARECEIVED(VARIABLE_TIER_NAME, VARIABLE_TIER_NAME_SHORT),
|
||||
|
||||
;
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
package nl.pim16aap2.armoredElytra.util.messages;
|
||||
|
||||
/**
|
||||
* Represents supported variables in localizable messages.
|
||||
*
|
||||
* @author Pim
|
||||
*/
|
||||
public interface MessageVariable {
|
||||
|
||||
String VARIABLE_TIER_NAME = "%ARMOR_TIER%";
|
||||
String VARIABLE_TIER_NAME_SHORT = "%ARMOR_TIER_SHORT%";
|
||||
|
||||
}
|
@ -3,12 +3,39 @@ main: nl.pim16aap2.armoredElytra.ArmoredElytra
|
||||
version: ${project.version}
|
||||
author: pim16aap2
|
||||
softdepend: [ AdvancedEnchantments ]
|
||||
api-version: 1.13
|
||||
api-version: 1.16
|
||||
commands:
|
||||
ArmoredElytra:
|
||||
description: Give an armored elytra of the specified tier.
|
||||
usage: /ArmoredElytra [receiver] <tier>
|
||||
permissions:
|
||||
armoredelytra.wear:
|
||||
description: Allows the player to wear all armored elytras.
|
||||
children:
|
||||
- armoredelytra.wear.leather
|
||||
- armoredelytra.wear.gold
|
||||
- armoredelytra.wear.chain
|
||||
- armoredelytra.wear.iron
|
||||
- armoredelytra.wear.diamond
|
||||
- armoredelytra.wear.netherite
|
||||
armoredelytra.craft:
|
||||
description: Allows the player to craft all armored elytras.
|
||||
children:
|
||||
- armoredelytra.craft.leather
|
||||
- armoredelytra.craft.gold
|
||||
- armoredelytra.craft.chain
|
||||
- armoredelytra.craft.iron
|
||||
- armoredelytra.craft.diamond
|
||||
- armoredelytra.craft.netherite
|
||||
armoredelytra.give:
|
||||
description: Allows the player to give all armored elytras.
|
||||
children:
|
||||
- armoredelytra.give.leather
|
||||
- armoredelytra.give.gold
|
||||
- armoredelytra.give.chain
|
||||
- armoredelytra.give.iron
|
||||
- armoredelytra.give.diamond
|
||||
- armoredelytra.give.netherite
|
||||
armoredelytra.wear.leather:
|
||||
description: Allow the player to wear leather tier armored elytras.
|
||||
armoredelytra.wear.gold:
|
||||
|
Loading…
Reference in New Issue
Block a user