Merge pull request #3 from PimvanderLoos/durability

Durability
This commit is contained in:
Pim van der Loos 2021-11-02 15:20:13 +01:00 committed by GitHub
commit 3b93735c68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 770 additions and 268 deletions

39
pom.xml
View File

@ -14,10 +14,13 @@
<version.maven.checkstyle>3.1.2</version.maven.checkstyle> <version.maven.checkstyle>3.1.2</version.maven.checkstyle>
<version.maven.compiler>3.8.1</version.maven.compiler> <version.maven.compiler>3.8.1</version.maven.compiler>
<version.maven.shade>3.2.1</version.maven.shade> <version.maven.shade>3.2.1</version.maven.shade>
<version.maven.surefire>3.0.0-M5</version.maven.surefire>
<version.spigot>1.16.5-R0.1-SNAPSHOT</version.spigot> <version.spigot>1.16.5-R0.1-SNAPSHOT</version.spigot>
<version.bstats>2.2.1</version.bstats> <version.bstats>2.2.1</version.bstats>
<version.checkstyle>9.1</version.checkstyle> <version.checkstyle>9.1</version.checkstyle>
<version.mockito>4.0.0</version.mockito>
<version.junit>5.8.1</version.junit>
</properties> </properties>
<repositories> <repositories>
@ -48,6 +51,32 @@
<version>${version.checkstyle}</version> <version>${version.checkstyle}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${version.mockito}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${version.mockito}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -96,6 +125,16 @@
<configLocation>checkstyle.xml</configLocation> <configLocation>checkstyle.xml</configLocation>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.maven.surefire}</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
<useFile>false</useFile>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -50,6 +50,7 @@ public class ArmoredElytra extends JavaPlugin implements Listener
private UpdateManager updateManager; private UpdateManager updateManager;
private INBTEditor nbtEditor; private INBTEditor nbtEditor;
private DurabilityManager durabilityManager;
@Override @Override
public void onEnable() public void onEnable()
@ -75,6 +76,9 @@ public class ArmoredElytra extends JavaPlugin implements Listener
nbtEditor = new NBTEditor(); nbtEditor = new NBTEditor();
config = new ConfigLoader(this); config = new ConfigLoader(this);
durabilityManager = new DurabilityManager(nbtEditor, config);
messages = new Messages(this); messages = new Messages(this);
readMessages(); readMessages();
@ -94,29 +98,26 @@ public class ArmoredElytra extends JavaPlugin implements Listener
"Stats disabled, not loading stats :(... Please consider enabling it! I am a simple man, " + "Stats disabled, not loading stats :(... Please consider enabling it! I am a simple man, " +
"seeing higher user numbers helps me stay motivated!"); "seeing higher user numbers helps me stay motivated!");
Bukkit.getPluginManager().registerEvents(new EventHandlers(this), this); Bukkit.getPluginManager().registerEvents(new EventHandlers(this, nbtEditor, durabilityManager), this);
getCommand("ArmoredElytra").setExecutor(new CommandHandler(this)); getCommand("ArmoredElytra").setExecutor(new CommandHandler(this, nbtEditor, durabilityManager));
// Load the plugin normally if not in uninstall mode. // Load the plugin normally if not in uninstall mode.
if (!config.uninstallMode()) if (!config.uninstallMode())
{ {
// Check if the user wants to disable durability penalty for flying with an armored elytra. Bukkit.getPluginManager().registerEvents(new FlyDurabilityHandler(config.noFlightDurability(),
if (config.noFlightDurability()) nbtEditor, durabilityManager), this);
{ final Listener creationListener =
Bukkit.getPluginManager().registerEvents(new FlyDurabilityHandler(), this); config.craftingInSmithingTable() ?
myLogger(Level.INFO, "Durability penalty for flying disabled!"); new SmithingTableCraftHandler(this, nbtEditor, durabilityManager, config) :
} new AnvilHandler(this, nbtEditor, durabilityManager, config);
else
myLogger(Level.INFO, "Durability penalty for flying enabled!");
final Listener creationListener = config.craftingInSmithingTable() ?
new SmithingTableCraftHandler(this) : new AnvilHandler(this);
Bukkit.getPluginManager().registerEvents(creationListener, this); Bukkit.getPluginManager().registerEvents(creationListener, this);
if (config.allowUpgradeToNetherite()) if (config.allowUpgradeToNetherite())
Bukkit.getPluginManager().registerEvents(new NetheriteUpgradeListener(this), this); Bukkit.getPluginManager()
.registerEvents(new NetheriteUpgradeListener(this, nbtEditor, durabilityManager, config), this);
if (config.dropNetheriteAsChestplate()) if (config.dropNetheriteAsChestplate())
Bukkit.getPluginManager().registerEvents(new ItemDropListener(this), this); Bukkit.getPluginManager().registerEvents(new ItemDropListener(nbtEditor), this);
// Log all allowed enchantments. // Log all allowed enchantments.
myLogger(Level.INFO, ("Allowed enchantments:")); myLogger(Level.INFO, ("Allowed enchantments:"));
@ -126,7 +127,7 @@ public class ArmoredElytra extends JavaPlugin implements Listener
else else
{ {
myLogger(Level.WARNING, "Plugin in uninstall mode!"); myLogger(Level.WARNING, "Plugin in uninstall mode!");
Bukkit.getPluginManager().registerEvents(new Uninstaller(this), this); Bukkit.getPluginManager().registerEvents(new Uninstaller(this, nbtEditor), this);
} }
} }

View File

@ -0,0 +1,270 @@
package nl.pim16aap2.armoredElytra;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.Util;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import javax.annotation.Nullable;
public class DurabilityManager
{
private static final int ELYTRA_MAX_DURABILITY = Material.ELYTRA.getMaxDurability();
private final int[] repairAmounts = new int[ArmorTier.values().length];
private final int[] maxDurabilities = new int[ArmorTier.values().length];
private final INBTEditor nbtEditor;
private final ConfigLoader config;
public DurabilityManager(INBTEditor nbtEditor, ConfigLoader config)
{
this.nbtEditor = nbtEditor;
this.config = config;
init();
}
/**
* Combination of {@link #getCombinedDurability(ItemStack, ItemStack, ArmorTier, ArmorTier)} and {@link
* #setDurability(ItemStack, int, ArmorTier)}.
* <p>
* First gets the combined of the input armored elytra and the other item and then applies it to the target armored
* elytra.
*
* @param armoredElytraOut The output armored elytra item. This is the elytra that will be updated.
* @param armoredElytraIn The input armored elytra item.
* @param other The other item that will be combined with the armored elytra. This can be another armored
* elytra, a chestplate, or any other item.
* @param currentTier The current armor tier of the armored elytra.
* @param targetTier The target tier of the armored elytra.
*/
public int setCombinedDurability(ItemStack armoredElytraOut, ItemStack armoredElytraIn, ItemStack other,
ArmorTier currentTier, ArmorTier targetTier)
{
final int combinedDurability = getCombinedDurability(armoredElytraIn, other, currentTier, targetTier);
setDurability(armoredElytraOut, combinedDurability, targetTier);
return combinedDurability;
}
/**
* Gets durability value resulting from combining an armored elytra with some other item with durability.
*
* @param armoredElytra The armored elytra item.
* @param other The other item that will be combined with the armored elytra. This can be another armored
* elytra, a chestplate, or any other item.
* @param currentTier The current armor tier of the armored elytra.
* @param targetTier The target tier of the armored elytra.
* @return The new real durability value of the armored elytra if it were to be combined with the other item.
*/
public int getCombinedDurability(ItemStack armoredElytra, ItemStack other,
ArmorTier currentTier, ArmorTier targetTier)
{
final ArmorTier otherTier = nbtEditor.getArmorTier(other);
final int currentMaxDurability = getMaxDurability(currentTier);
final int targetMaxDurability = getMaxDurability(targetTier);
final int otherMaxDurability = otherTier != ArmorTier.NONE ?
getMaxDurability(otherTier) : other.getType().getMaxDurability();
//noinspection deprecation
final int otherDurability = other.getType().equals(Material.ELYTRA) ?
getRealDurability(other, null) : other.getDurability();
final int currentDurability = getRealDurability(armoredElytra, currentTier);
final int combinedDurability = targetMaxDurability -
(otherMaxDurability - otherDurability) -
(currentMaxDurability - currentDurability);
return Util.between(combinedDurability, 0, targetMaxDurability);
}
/**
* Removes durability from an armored elytra.
*
* @param armoredElytra The armored elytra item to damage.
* @param durabilityLoss The amount of durability to remove from the armored elytra.
* @param providedTier The tier of the armored elytra (if this is available). If this is null, it will be
* retrieved from the item itself.
* @return The new durability after removing the provided amount.
*/
public int removeDurability(ItemStack armoredElytra, int durabilityLoss, @Nullable ArmorTier providedTier)
{
final ArmorTier currentTier = providedTier == null ? nbtEditor.getArmorTier(armoredElytra) : providedTier;
final int currentDurability = getRealDurability(armoredElytra, currentTier);
final int newDurability = Util.between(currentDurability + durabilityLoss, 0, getMaxDurability(currentTier));
setDurability(armoredElytra, newDurability, providedTier);
return newDurability;
}
/**
* Gets the required number of repair items required to fully repair an armored elytra.
* <p>
* For example, for an ArmoredElytra that is damaged for 50 durability and its repair item restores 40 durability,
* this method would return 2.
*
* @param armoredElytra The armored elytra item for which to check how many items are needed to fully repair it.
* @param providedTier The tier of the armored elytra (if this is available). If this is null, it will be retrieved
* from the item itself.
* @return The required number of repair items required to fully repair the armored elytra.
*/
public int getFullRepairItemCount(ItemStack armoredElytra, @Nullable ArmorTier providedTier)
{
final ArmorTier currentTier = providedTier == null ? nbtEditor.getArmorTier(armoredElytra) : providedTier;
final int repairableDurability = getMaxDurability(currentTier) - getRealDurability(armoredElytra, currentTier);
return (int) Math.ceil((float) repairableDurability / getRepairAmount(currentTier));
}
/**
* Gets the new durability of an armored elytra if it were to be repaired right now.
*
* @param armoredElytra The armored elytra item for which to check what the new durability would be after repairing
* it.
* @param repairCount The number of repair items.
* @param providedTier The tier of the armored elytra (if this is available). If this is null, it will be retrieved
* from the item itself.
* @return The real durability value of the armored elytra if it were to be repaired.
*/
public int getRepairedDurability(ItemStack armoredElytra, int repairCount, @Nullable ArmorTier providedTier)
{
final ArmorTier currentTier = providedTier == null ? nbtEditor.getArmorTier(armoredElytra) : providedTier;
final int restoredDurability = repairCount * getRepairAmount(currentTier);
final int currentDurability = getRealDurability(armoredElytra, currentTier);
return Math.max(0, currentDurability - restoredDurability);
}
/**
* Gets the real durability of an item.
* <p>
* If the item is an armored elytra, and it does not have a real durability yet, it will be upgraded.
*
* @param item The item for which to figure out the real durability.
* @param providedTier The tier of the armored elytra (if this is available). If this is null, it will be retrieved
* from the item itself.
* @return The real durability of the item.
*/
public int getRealDurability(ItemStack item, @Nullable ArmorTier providedTier)
{
final ArmorTier currentTier = providedTier == null ? nbtEditor.getArmorTier(item) : providedTier;
if (currentTier == ArmorTier.NONE)
//noinspection deprecation
return item.getDurability();
final int realDurability = nbtEditor.getRealDurability(item, currentTier);
return realDurability == -1 ? upgradeArmoredElytraToDurability(item, currentTier) : realDurability;
}
/**
* Sets the durability values (real + shown) of an armored elytra.
*
* @param item The armored elytra item for which to set the durability values.
* @param durability The real durability value.
* @param providedTier The tier of the armored elytra (if this is available). If this is null, it will be retrieved
* from the item itself.
*/
public void setDurability(ItemStack item, int durability, @Nullable ArmorTier providedTier)
{
final ArmorTier currentTier = providedTier == null ? nbtEditor.getArmorTier(item) : providedTier;
final int oldMaxDurability = getMaxDurability(currentTier);
final int rawDurability = getRemappedDurability(durability, oldMaxDurability, ELYTRA_MAX_DURABILITY);
nbtEditor.updateDurability(item, durability, rawDurability);
}
/**
* Sets the real durability NBT data for armored elytras that do not have it.
* <p>
* The real durability is calculated from the current 'raw' durability. The real durability will be the same
* percentage of the max durability for the type as the raw durability is of an elytra's maximum durability.
*
* @param armoredElytra The armored elytra to upgrade to an armored elytra with durability.
* @param currentTier The current tier of the armored elytra.
* @return The real durability of the armored elytra.
*/
private int upgradeArmoredElytraToDurability(ItemStack armoredElytra, ArmorTier currentTier)
{
final int maxDurability = getMaxDurability(currentTier);
//noinspection deprecation
final int rawDurability = armoredElytra.getDurability();
final int realDurability = maxDurability == ELYTRA_MAX_DURABILITY ?
rawDurability :
getRemappedDurability(rawDurability, ELYTRA_MAX_DURABILITY, maxDurability);
nbtEditor.updateDurability(armoredElytra, realDurability, rawDurability);
return realDurability;
}
/**
* Gets the maximum durability for an armor tier. This may or may not be {@link
* ArmorTier#getMaxDurability(ArmorTier)} depending on {@link ConfigLoader#useTierDurability()}.
*
* @param armorTier The armor tier for which to figure out the maximum durability.
* @return The maximum durability of the armor tier.
*/
private int calculateMaxDurability(ArmorTier armorTier)
{
if (armorTier == ArmorTier.NONE || !config.useTierDurability())
return ELYTRA_MAX_DURABILITY;
return ArmorTier.getMaxDurability(armorTier);
}
/**
* Gets the maximum durability for a given armor tier.
*
* @param armorTier The armor tier for which to get the maximum durability.
* @return The maximum durability of the given armor tier.
*/
public int getMaxDurability(ArmorTier armorTier)
{
return maxDurabilities[armorTier.ordinal()];
}
/**
* Gets the amount of durability restored per repair step for a given armor tier.
*
* @param armorTier The armor tier.
* @return The amount of durability restored per repair step for the given armor tier.
*/
public int getRepairAmount(ArmorTier armorTier)
{
return repairAmounts[armorTier.ordinal()];
}
/**
* Remaps a durability value from an old maximum value to a new maximum while maintaining the same durability
* percentage.
*
* @param durability The current durability value.
* @param oldMax The old maximum durability.
* @param newMax The new maximum durability.
* @return The new durability value after remapping it to the new maximum. The value cannot be less than 0 or more
* than newMax.
*/
private int getRemappedDurability(int durability, int oldMax, int newMax)
{
final float relativeDurability = (float) durability / oldMax;
return Util.between((int) Math.ceil(relativeDurability * newMax), 0, newMax);
}
/**
* Initializes the {@link #maxDurabilities} and {@link #repairAmounts} arrays.
*/
private void init()
{
repairAmounts[0] = 0;
maxDurabilities[0] = ELYTRA_MAX_DURABILITY;
final ArmorTier[] armorTiers = ArmorTier.values();
for (int idx = 1; idx < armorTiers.length; ++idx)
{
final ArmorTier armorTier = armorTiers[idx];
final int maxDurability = calculateMaxDurability(armorTier);
maxDurabilities[idx] = maxDurability;
final int steps = Math.max(1, config.getFullRepairItemCount(armorTier));
repairAmounts[idx] = (int) Math.ceil((float) maxDurability / steps);
}
}
}

View File

@ -1,6 +1,7 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor; import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.Action; import nl.pim16aap2.armoredElytra.util.Action;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
@ -26,19 +27,16 @@ import java.util.logging.Level;
public class AnvilHandler extends ArmoredElytraHandler implements Listener public class AnvilHandler extends ArmoredElytraHandler implements Listener
{ {
private final ConfigLoader configLoader; protected AnvilHandler(ArmoredElytra plugin, boolean creationEnabled,
private final INBTEditor nbtEditor; INBTEditor nbtEditor, DurabilityManager durabilityManager, ConfigLoader config)
public AnvilHandler(final ArmoredElytra plugin, final boolean creationEnabled)
{ {
super(plugin, creationEnabled); super(plugin, creationEnabled, nbtEditor, durabilityManager, config);
configLoader = plugin.getConfigLoader();
nbtEditor = plugin.getNbtEditor();
} }
public AnvilHandler(final ArmoredElytra plugin) public AnvilHandler(ArmoredElytra plugin, INBTEditor nbtEditor,
DurabilityManager durabilityManager, ConfigLoader config)
{ {
this(plugin, true); super(plugin, true, nbtEditor, durabilityManager, config);
} }
// Valid inputs: // Valid inputs:
@ -46,7 +44,7 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
// - Elytra (armored) + enchanted book -> Enchant // - Elytra (armored) + enchanted book -> Enchant
// - Elytra (armored) + its repair item -> Repair // - Elytra (armored) + its repair item -> Repair
// - Elytra (armored) + other elytra (armored) -> Combine (Enchant + Repair) // - Elytra (armored) + other elytra (armored) -> Combine (Enchant + Repair)
// ! Elytra (armored, !leather) + leather/membrane -> Block // - Elytra (armored, !leather) + leather/membrane -> Block
// //
// Ignoring: // Ignoring:
// - Elytra (not armored) + !chestplate -> None // - Elytra (not armored) + !chestplate -> None
@ -56,14 +54,6 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
if (itemOne == null || itemTwo == null) if (itemOne == null || itemTwo == null)
return Action.NONE; return Action.NONE;
// If itemTwo is the elytra, while itemOne isn't, switch itemOne and itemTwo.
if (itemTwo.getType() == Material.ELYTRA && itemOne.getType() != Material.ELYTRA)
{
ItemStack tmp = itemOne;
itemOne = itemTwo;
itemTwo = tmp;
}
if (itemOne.getType() != Material.ELYTRA) if (itemOne.getType() != Material.ELYTRA)
return Action.NONE; return Action.NONE;
@ -79,14 +69,13 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
{ {
// If the armored elytra is to be enchanted using an enchanted book... // If the armored elytra is to be enchanted using an enchanted book...
if (matTwo == Material.ENCHANTED_BOOK) if (matTwo == Material.ENCHANTED_BOOK)
return configLoader.allowAddingEnchantments() ? Action.ENCHANT : Action.BLOCK; return config.allowAddingEnchantments() ? Action.ENCHANT : Action.BLOCK;
// If the armored elytra is to be repaired using its repair item... // If the armored elytra is to be repaired using its repair item...
if (ArmorTier.getRepairItem(tier) == matTwo) if (ArmorTier.getRepairItem(tier) == matTwo)
return itemOne.getDurability() == 0 ? Action.NONE : Action.REPAIR; return durabilityManager.getRealDurability(itemOne, tier) == 0 ? Action.BLOCK : Action.REPAIR;
// If the armored elytra is to be combined with another armored elytra of the // If the armored elytra is to be combined with another armored elytra of the same tier...
// same tier...
if (nbtEditor.getArmorTier(itemTwo) == tier) if (nbtEditor.getArmorTier(itemTwo) == tier)
return creationEnabled ? Action.COMBINE : Action.NONE; return creationEnabled ? Action.COMBINE : Action.NONE;
@ -101,13 +90,13 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
} }
// Handle all anvil related stuff for this plugin. // Handle all anvil related stuff for this plugin.
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.LOWEST)
private void onAnvilInventoryOpen(PrepareAnvilEvent event) private void onAnvilInventoryOpen(PrepareAnvilEvent event)
{ {
Player player = (Player) event.getView().getPlayer(); Player player = (Player) event.getView().getPlayer();
ItemStack itemA = event.getInventory().getItem(0); ItemStack itemA = event.getInventory().getItem(0);
ItemStack itemB = event.getInventory().getItem(1); ItemStack itemB = event.getInventory().getItem(1);
ItemStack result = null; ItemStack result;
if (itemA != null && itemB != null) if (itemA != null && itemB != null)
// If itemB is the (armored) elytra, while itemA isn't, switch itemA and itemB. // If itemB is the (armored) elytra, while itemA isn't, switch itemA and itemB.
@ -121,37 +110,36 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
// Check if there are items in both input slots. // Check if there are items in both input slots.
if (itemA != null && itemB != null) if (itemA != null && itemB != null)
{ {
Action action = isValidInput(itemA, itemB); final Action action = isValidInput(itemA, itemB);
ArmorTier newTier = ArmorTier.NONE; ArmorTier newTier = ArmorTier.NONE;
ArmorTier curTier = nbtEditor.getArmorTier(itemA); final ArmorTier curTier = nbtEditor.getArmorTier(itemA);
short durability = 0;
EnchantmentContainer enchantments = EnchantmentContainer.getEnchantments(itemA, plugin); int newDurability = 0;
final EnchantmentContainer enchantments = EnchantmentContainer.getEnchantments(itemA, plugin);
switch (action) switch (action)
{ {
case REPAIR: case REPAIR:
newTier = curTier; newTier = curTier;
durability = repairItem(itemA.getDurability(), itemB); newDurability = durabilityManager.getRepairedDurability(itemA, itemB.getAmount(), curTier);
break; break;
case COMBINE: case COMBINE:
newTier = curTier; newTier = curTier;
durability = (short) (-itemA.getType().getMaxDurability() - itemA.getDurability() newDurability = durabilityManager.getCombinedDurability(itemA, itemB, curTier, newTier);
- itemB.getDurability());
durability = durability < 0 ? 0 : durability;
enchantments.merge(EnchantmentContainer.getEnchantments(itemB, plugin)); enchantments.merge(EnchantmentContainer.getEnchantments(itemB, plugin));
break; break;
case CREATE: case CREATE:
newTier = Util.armorToTier(itemB.getType()); newTier = Util.armorToTier(itemB.getType());
durability = 0; newDurability = durabilityManager.getCombinedDurability(itemA, itemB, curTier, newTier);
enchantments.merge(EnchantmentContainer.getEnchantments(itemB, plugin)); enchantments.merge(EnchantmentContainer.getEnchantments(itemB, plugin));
break; break;
case ENCHANT: case ENCHANT:
newTier = curTier; newTier = curTier;
durability = itemA.getDurability(); newDurability = durabilityManager.getRealDurability(itemA, newTier);
// If there aren't any illegal enchantments on the book, continue as normal. // If there aren't any illegal enchantments on the book, continue as normal.
// Otherwise... Block. // Otherwise... Block.
EnchantmentContainer enchantmentsB = EnchantmentContainer.getEnchantments(itemB, plugin); final EnchantmentContainer enchantmentsB = EnchantmentContainer.getEnchantments(itemB, plugin);
if (enchantmentsB.getEnchantmentCount() > 0) if (enchantmentsB.getEnchantmentCount() > 0)
{ {
enchantments.merge(enchantmentsB); enchantments.merge(enchantmentsB);
@ -170,12 +158,12 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
{ {
result = new ItemStack(Material.ELYTRA, 1); result = new ItemStack(Material.ELYTRA, 1);
enchantments.applyEnchantments(result); enchantments.applyEnchantments(result);
result.setDurability(durability); durabilityManager.setDurability(result, newDurability, newTier);
final String name = getElytraResultName(itemA, action, newTier, event.getInventory().getRenameText()); final String name = getElytraResultName(itemA, action, newTier, event.getInventory().getRenameText());
final Color color = getItemColor(itemA, itemB); final Color color = getItemColor(itemA, itemB);
result = nbtEditor.addArmorNBTTags(result, newTier, configLoader.unbreakable(), name, color); result = nbtEditor.addArmorNBTTags(result, newTier, config.unbreakable(), name, color);
event.setResult(result); event.setResult(result);
return; return;
@ -183,7 +171,7 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
} }
// If one of the input items is null and the other an armored elytra, remove the result. // If one of the input items is null and the other an armored elytra, remove the result.
// This prevent some naming issues. // This prevents some naming issues.
if ((itemA == null ^ itemB == null) && if ((itemA == null ^ itemB == null) &&
nbtEditor.getArmorTier(itemA == null ? itemB : itemA) != ArmorTier.NONE) nbtEditor.getArmorTier(itemA == null ? itemB : itemA) != ArmorTier.NONE)
event.setResult(null); event.setResult(null);
@ -193,13 +181,13 @@ public class AnvilHandler extends ArmoredElytraHandler implements Listener
final ArmorTier armorTier, final String renameText) final ArmorTier armorTier, final String renameText)
{ {
final String tierName = plugin.getArmoredElytraName(armorTier); final String tierName = plugin.getArmoredElytraName(armorTier);
if (renameText == null || !configLoader.allowRenaming()) if (renameText == null || !config.allowRenaming())
return tierName; return tierName;
final ItemMeta meta = baseItem.getItemMeta(); final ItemMeta meta = baseItem.getItemMeta();
final String currentName = meta == null ? null : meta.getDisplayName(); final String currentName = meta == null ? null : meta.getDisplayName();
// When the rename text is empty, give it the default tier-name when creating a new armored elytra // When the renameText is empty, give it the default tier-name when creating a new armored elytra
// (so it's named properly) or when the current name is already the tier name (just returning the current // (so it's named properly) or when the current name is already the tier name (just returning the current
// name would strip the tier's color in this case). // name would strip the tier's color in this case).
if ((action == Action.CREATE && renameText.equals("")) || if ((action == Action.CREATE && renameText.equals("")) ||

View File

@ -1,7 +1,9 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.util.XMaterial; import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.Material; import org.bukkit.Material;
@ -18,41 +20,22 @@ import javax.annotation.CheckReturnValue;
*/ */
abstract class ArmoredElytraHandler abstract class ArmoredElytraHandler
{ {
protected final ArmoredElytra plugin;
protected final boolean creationEnabled;
private static final Color DEFAULT_LEATHER_COLOR = Bukkit.getServer().getItemFactory().getDefaultLeatherColor(); private static final Color DEFAULT_LEATHER_COLOR = Bukkit.getServer().getItemFactory().getDefaultLeatherColor();
public ArmoredElytraHandler(final ArmoredElytra plugin, final boolean creationEnabled) protected final ArmoredElytra plugin;
protected final boolean creationEnabled;
protected final ConfigLoader config;
protected final INBTEditor nbtEditor;
protected final DurabilityManager durabilityManager;
protected ArmoredElytraHandler(ArmoredElytra plugin, boolean creationEnabled, INBTEditor nbtEditor,
DurabilityManager durabilityManager, ConfigLoader config)
{ {
this.plugin = plugin; this.plugin = plugin;
this.creationEnabled = creationEnabled; this.creationEnabled = creationEnabled;
} this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
// Repair an Armored Elytra this.config = config;
protected short repairItem(short curDur, ItemStack repairItem)
{
// Get the multiplier for the repair items.
double mult = 0.01;
if (repairItem.getType().equals(Material.LEATHER))
mult *= (100.0f / plugin.getConfigLoader().LEATHER_TO_FULL());
else if (repairItem.getType().equals(Material.GOLD_INGOT))
mult *= (100.0f / plugin.getConfigLoader().GOLD_TO_FULL());
else if (repairItem.getType().equals(Material.IRON_INGOT))
mult *= (100.0f / plugin.getConfigLoader().IRON_TO_FULL());
else if (repairItem.getType().equals(Material.DIAMOND))
mult *= (100.0f / plugin.getConfigLoader().DIAMONDS_TO_FULL());
else if (repairItem.getType().equals(XMaterial.NETHERITE_INGOT.parseMaterial()))
mult *= (100.0f / plugin.getConfigLoader().NETHERITE_TO_FULL());
int maxDurability = Material.ELYTRA.getMaxDurability();
int newDurability = (int) (curDur - repairItem.getAmount() * (maxDurability * mult));
return (short) (Math.max(newDurability, 0));
} }
/** /**
@ -80,7 +63,7 @@ abstract class ArmoredElytraHandler
return null; return null;
if (itemStack.getType() == Material.ELYTRA) if (itemStack.getType() == Material.ELYTRA)
return ArmoredElytra.getInstance().getNbtEditor().getColorOfArmoredElytra(itemStack); return nbtEditor.getColorOfArmoredElytra(itemStack);
if (!itemStack.hasItemMeta() || !(itemStack.getItemMeta() instanceof LeatherArmorMeta)) if (!itemStack.hasItemMeta() || !(itemStack.getItemMeta() instanceof LeatherArmorMeta))
return null; return null;

View File

@ -1,6 +1,8 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.messages.Message; import nl.pim16aap2.armoredElytra.util.messages.Message;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -21,11 +23,15 @@ import java.util.logging.Level;
public class CommandHandler implements CommandExecutor public class CommandHandler implements CommandExecutor
{ {
private final ArmoredElytra plugin; private final ArmoredElytra plugin;
private final INBTEditor nbtEditor;
private final DurabilityManager durabilityManager;
private static Field BY_KEY_FIELD; private static Field BY_KEY_FIELD;
public CommandHandler(ArmoredElytra plugin) public CommandHandler(ArmoredElytra plugin, INBTEditor nbtEditor, DurabilityManager durabilityManager)
{ {
this.plugin = plugin; this.plugin = plugin;
this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
} }
@Override @Override
@ -80,9 +86,9 @@ public class CommandHandler implements CommandExecutor
if (allowed) if (allowed)
{ {
plugin.elytraReceivedMessage(receiver, armorTier); plugin.elytraReceivedMessage(receiver, armorTier);
newElytra = ArmoredElytra.getInstance().getNbtEditor() newElytra = nbtEditor.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier,
.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier, plugin.getConfigLoader().unbreakable());
plugin.getConfigLoader().unbreakable()); durabilityManager.setDurability(newElytra, 0, armorTier);
plugin.giveArmoredElytraToPlayer(receiver, newElytra); plugin.giveArmoredElytraToPlayer(receiver, newElytra);
} }
else else
@ -119,9 +125,10 @@ public class CommandHandler implements CommandExecutor
return false; return false;
plugin.elytraReceivedMessage(player, armorTier); plugin.elytraReceivedMessage(player, armorTier);
newElytra = ArmoredElytra.getInstance().getNbtEditor() newElytra = nbtEditor.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier,
.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), armorTier, plugin.getConfigLoader().unbreakable());
plugin.getConfigLoader().unbreakable()); durabilityManager.setDurability(newElytra, 0, armorTier);
plugin.giveArmoredElytraToPlayer(player, newElytra); plugin.giveArmoredElytraToPlayer(player, newElytra);
plugin.myLogger(Level.INFO, ("Giving an armored elytra of the " + ArmorTier.getArmor(armorTier) + plugin.myLogger(Level.INFO, ("Giving an armored elytra of the " + ArmorTier.getArmor(armorTier) +
" armor tier to player " + player.getName())); " armor tier to player " + player.getName()));

View File

@ -1,10 +1,12 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.lib.armorequip.ArmorEquipEvent; import nl.pim16aap2.armoredElytra.lib.armorequip.ArmorEquipEvent;
import nl.pim16aap2.armoredElytra.lib.armorequip.ArmorListener; import nl.pim16aap2.armoredElytra.lib.armorequip.ArmorListener;
import nl.pim16aap2.armoredElytra.lib.armorequip.ArmorType; import nl.pim16aap2.armoredElytra.lib.armorequip.ArmorType;
import nl.pim16aap2.armoredElytra.lib.armorequip.DispenserArmorListener; import nl.pim16aap2.armoredElytra.lib.armorequip.DispenserArmorListener;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.AllowedToWearEnum; import nl.pim16aap2.armoredElytra.util.AllowedToWearEnum;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.Util; import nl.pim16aap2.armoredElytra.util.Util;
@ -14,6 +16,7 @@ import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
@ -24,12 +27,16 @@ import java.util.Random;
public class EventHandlers implements Listener public class EventHandlers implements Listener
{ {
private final ArmoredElytra plugin;
private final Random random = new Random(); private final Random random = new Random();
private final ArmoredElytra plugin;
private final INBTEditor nbtEditor;
private final DurabilityManager durabilityManager;
public EventHandlers(ArmoredElytra plugin) public EventHandlers(ArmoredElytra plugin, INBTEditor nbtEditor, DurabilityManager durabilityManager)
{ {
this.plugin = plugin; this.plugin = plugin;
this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
initializeArmorEquipEvent(); initializeArmorEquipEvent();
} }
@ -39,13 +46,6 @@ public class EventHandlers implements Listener
Bukkit.getPluginManager().registerEvents(new DispenserArmorListener(), plugin); Bukkit.getPluginManager().registerEvents(new DispenserArmorListener(), plugin);
} }
private void moveChestplateToInventory(Player player)
{
player.getInventory().addItem(player.getInventory().getChestplate());
player.getInventory().getChestplate().setAmount(0);
player.updateInventory();
}
// Make sure the player has the correct permission and that the item is not // Make sure the player has the correct permission and that the item is not
// broken. // broken.
private AllowedToWearEnum isAllowedToWear(ItemStack elytra, Player player, ArmorTier armorTier) private AllowedToWearEnum isAllowedToWear(ItemStack elytra, Player player, ArmorTier armorTier)
@ -60,62 +60,44 @@ public class EventHandlers implements Listener
} }
// Handle armored elytra durability loss. // Handle armored elytra durability loss.
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onPlayerDamage(EntityDamageEvent e) public void onPlayerDamage(EntityDamageEvent e)
{ {
if (!(e.getEntity() instanceof Player)) if (!(e.getEntity() instanceof Player))
return; return;
final Player p = (Player) e.getEntity();
if (plugin.getConfigLoader().unbreakable()) final ItemStack elytra = p.getInventory().getChestplate();
if (elytra == null)
return; return;
Player p = (Player) e.getEntity(); final ArmorTier armorTier = nbtEditor.getArmorTier(elytra);
// If the player didn't die from the damage. if (armorTier == ArmorTier.NONE)
if ((p.getHealth() - e.getFinalDamage()) > 0) return;
final DamageCause cause = e.getCause();
// The elytra doesn't receive any damage for these causes:
if (cause == DamageCause.DROWNING || cause == DamageCause.STARVATION || cause == DamageCause.SUFFOCATION ||
cause == DamageCause.SUICIDE || cause == DamageCause.FLY_INTO_WALL || cause == DamageCause.POISON)
return;
final boolean removeDurability;
if (elytra.containsEnchantment(Enchantment.DURABILITY))
{ {
if (p.getInventory().getChestplate() == null) final int randomInt = random.nextInt(101);
return; final int enchantLevel = elytra.getEnchantmentLevel(Enchantment.DURABILITY);
// Formula taken from: https://minecraft.fandom.com/wiki/Unbreaking#Usage
if (ArmoredElytra.getInstance().getNbtEditor().getArmorTier(p.getInventory().getChestplate()) == final float removeDurabilityChance = 60 + 40f / (enchantLevel + 1);
ArmorTier.NONE) removeDurability = randomInt <= removeDurabilityChance;
return;
ItemStack elytra = p.getInventory().getChestplate();
DamageCause cause = e.getCause();
// The elytra doesn't receive any damage for these causes:
if (cause != DamageCause.DROWNING && cause != DamageCause.STARVATION && cause != DamageCause.SUFFOCATION &&
cause != DamageCause.SUICIDE && cause != DamageCause.FLY_INTO_WALL && cause != DamageCause.POISON)
{
int durability = p.getInventory().getChestplate().getDurability();
int maxDurability = p.getInventory().getChestplate().getType().getMaxDurability();
int newDurability = durability + ((int) (e.getDamage() / 4) > 1 ? (int) (e.getDamage() / 4) : 1);
// If the elytra has the durability enchantment, we calculate the durability
// loss ourselves.
if (p.getInventory().getChestplate().containsEnchantment(Enchantment.DURABILITY))
{
// Get a random int between 0 and 100 to use in deciding if the durability
// enchantment will take effect.
int randomInt = random.nextInt(101);
int enchantLevel = p.getInventory().getChestplate().getEnchantmentLevel(Enchantment.DURABILITY);
int durabilityDelta = (100 / (enchantLevel + 1)) < randomInt ? 0 : 1;
// If the durability equals/exceeds maxDurability, it's broken (0 = full item
// durability).
if (durability >= maxDurability)
moveChestplateToInventory(p);
else
newDurability = durability + durabilityDelta;
}
// If the item should be broken, make sure it really is broken and unequip it.
if (newDurability >= maxDurability)
{
newDurability = maxDurability;
moveChestplateToInventory(p);
}
elytra.setDurability((short) (newDurability));
}
} }
else
removeDurability = true;
// Even when we don't subtract durability, we still want to update the durability, so just subtract 0.
final int durabilityLoss = removeDurability ? (int) Math.max(1, e.getDamage() / 4) : 0;
final int newDurability = durabilityManager.removeDurability(elytra, durabilityLoss, armorTier);
if (newDurability >= durabilityManager.getMaxDurability(armorTier))
Util.moveChestplateToInventory(p);
} }
@EventHandler @EventHandler
@ -130,8 +112,8 @@ public class EventHandlers implements Listener
!e.getNewArmorPiece().getType().equals(Material.ELYTRA)) !e.getNewArmorPiece().getType().equals(Material.ELYTRA))
return; return;
ArmorTier armorTier = ArmoredElytra.getInstance().getNbtEditor().getArmorTier(e.getNewArmorPiece()); final ArmorTier armorTier = nbtEditor.getArmorTier(e.getNewArmorPiece());
AllowedToWearEnum allowed = isAllowedToWear(e.getNewArmorPiece(), e.getPlayer(), armorTier); final AllowedToWearEnum allowed = isAllowedToWear(e.getNewArmorPiece(), e.getPlayer(), armorTier);
switch (allowed) switch (allowed)
{ {
case ALLOWED: case ALLOWED:

View File

@ -1,21 +1,30 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.Util;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerItemDamageEvent; import org.bukkit.event.player.PlayerItemDamageEvent;
public class FlyDurabilityHandler implements Listener public class FlyDurabilityHandler implements Listener
{ {
public FlyDurabilityHandler() private final boolean disableDurability;
private final INBTEditor nbtEditor;
private final DurabilityManager durabilityManager;
public FlyDurabilityHandler(boolean disableDurability, INBTEditor nbtEditor, DurabilityManager durabilityManager)
{ {
this.disableDurability = disableDurability;
this.nbtEditor = nbtEditor;
this.durabilityManager = durabilityManager;
} }
// Do not decrease elytra durability while flying. This also cancels durability decrease when // Do not decrease elytra durability while flying.
// it should (i.e. getting hit) while flying, but I don't really care. @EventHandler(priority = EventPriority.LOWEST)
@EventHandler
public void onItemDamage(PlayerItemDamageEvent e) public void onItemDamage(PlayerItemDamageEvent e)
{ {
if (e.getItem().getType() != Material.ELYTRA) if (e.getItem().getType() != Material.ELYTRA)
@ -24,7 +33,18 @@ public class FlyDurabilityHandler implements Listener
if (!e.getPlayer().isGliding()) if (!e.getPlayer().isGliding())
return; return;
if (ArmoredElytra.getInstance().getNbtEditor().getArmorTier(e.getItem()) != ArmorTier.NONE) final ArmorTier armorTier = nbtEditor.getArmorTier(e.getItem());
e.setCancelled(true); if (armorTier == ArmorTier.NONE)
return;
// This also cancels durability decrease when it should (i.e. getting hit) while flying,
// but that is likely to be rare enough for it to not matter.
e.setCancelled(true);
if (disableDurability)
return;
final int newDurability = durabilityManager.removeDurability(e.getItem(), e.getDamage(), armorTier);
if (newDurability >= durabilityManager.getMaxDurability(armorTier))
Util.moveChestplateToInventory(e.getPlayer());
} }
} }

View File

@ -1,6 +1,6 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -13,11 +13,11 @@ import org.bukkit.inventory.ItemStack;
public class ItemDropListener implements Listener public class ItemDropListener implements Listener
{ {
protected final ArmoredElytra plugin; private final INBTEditor nbtEditor;
public ItemDropListener(final ArmoredElytra plugin) public ItemDropListener(INBTEditor nbtEditor)
{ {
this.plugin = plugin; this.nbtEditor = nbtEditor;
} }
/** /**
@ -32,10 +32,10 @@ public class ItemDropListener implements Listener
private ItemStack getNewDrop(final ItemStack itemStack) private ItemStack getNewDrop(final ItemStack itemStack)
{ {
if (itemStack == null || itemStack.getType() != Material.ELYTRA || if (itemStack == null || itemStack.getType() != Material.ELYTRA ||
plugin.getNbtEditor().getArmorTier(itemStack) != ArmorTier.NETHERITE) nbtEditor.getArmorTier(itemStack) != ArmorTier.NETHERITE)
return null; return null;
ItemStack newDrop = new ItemStack(Material.NETHERITE_CHESTPLATE, 1); final ItemStack newDrop = new ItemStack(Material.NETHERITE_CHESTPLATE, 1);
newDrop.setItemMeta(itemStack.getItemMeta()); newDrop.setItemMeta(itemStack.getItemMeta());
return newDrop; return newDrop;
@ -53,10 +53,10 @@ public class ItemDropListener implements Listener
private ItemStack getNewPickup(final ItemStack itemStack) private ItemStack getNewPickup(final ItemStack itemStack)
{ {
if (itemStack == null || itemStack.getType() != Material.NETHERITE_CHESTPLATE || if (itemStack == null || itemStack.getType() != Material.NETHERITE_CHESTPLATE ||
plugin.getNbtEditor().getArmorTier(itemStack) != ArmorTier.NETHERITE) nbtEditor.getArmorTier(itemStack) != ArmorTier.NETHERITE)
return null; return null;
ItemStack newDrop = new ItemStack(Material.ELYTRA, 1); final ItemStack newDrop = new ItemStack(Material.ELYTRA, 1);
newDrop.setItemMeta(itemStack.getItemMeta()); newDrop.setItemMeta(itemStack.getItemMeta());
return newDrop; return newDrop;

View File

@ -1,7 +1,10 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -12,12 +15,13 @@ import org.bukkit.inventory.SmithingInventory;
public class NetheriteUpgradeListener extends SmithingTableListener public class NetheriteUpgradeListener extends SmithingTableListener
{ {
public NetheriteUpgradeListener(final ArmoredElytra plugin) public NetheriteUpgradeListener(final ArmoredElytra plugin, INBTEditor nbtEditor,
DurabilityManager durabilityManager, ConfigLoader config)
{ {
super(plugin); super(plugin, false, nbtEditor, durabilityManager, config);
} }
@EventHandler(ignoreCancelled = true) @Override @EventHandler(ignoreCancelled = true)
public void onSmithingTableUsage(final PrepareSmithingEvent event) public void onSmithingTableUsage(final PrepareSmithingEvent event)
{ {
super.onSmithingTableUsage(event); super.onSmithingTableUsage(event);

View File

@ -1,7 +1,10 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.Util; import nl.pim16aap2.armoredElytra.util.Util;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
@ -14,13 +17,16 @@ import org.bukkit.inventory.SmithingInventory;
public class SmithingTableCraftHandler extends SmithingTableListener public class SmithingTableCraftHandler extends SmithingTableListener
{ {
public SmithingTableCraftHandler(final ArmoredElytra plugin) public SmithingTableCraftHandler(final ArmoredElytra plugin, INBTEditor nbtEditor,
DurabilityManager durabilityManager, ConfigLoader config)
{ {
super(plugin, true); super(plugin, true, nbtEditor, durabilityManager, config);
// Register the anvil handler with creation disabled so AEs can still be repaired and stuff. // Register the anvil handler with creation disabled so AEs can still be repaired and stuff.
Bukkit.getPluginManager().registerEvents(new AnvilHandler(plugin, false), plugin); Bukkit.getPluginManager()
.registerEvents(new AnvilHandler(plugin, false, nbtEditor, durabilityManager, config), plugin);
} }
@Override
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onSmithingTableUsage(final PrepareSmithingEvent event) public void onSmithingTableUsage(final PrepareSmithingEvent event)
{ {
@ -42,7 +48,7 @@ public class SmithingTableCraftHandler extends SmithingTableListener
{ {
if (!isAESmithingTableEvent(e)) if (!isAESmithingTableEvent(e))
return; return;
SmithingInventory smithingInventory = (SmithingInventory) e.getInventory(); final SmithingInventory smithingInventory = (SmithingInventory) e.getInventory();
final ItemStack result = smithingInventory.getItem(2); final ItemStack result = smithingInventory.getItem(2);
// This cast may look unchecked, but it was checked by isSmithingTableEvent already. // This cast may look unchecked, but it was checked by isSmithingTableEvent already.

View File

@ -1,7 +1,10 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.DurabilityManager;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
import nl.pim16aap2.armoredElytra.util.EnchantmentContainer; import nl.pim16aap2.armoredElytra.util.EnchantmentContainer;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.Material; import org.bukkit.Material;
@ -17,15 +20,10 @@ import java.util.logging.Level;
abstract class SmithingTableListener extends ArmoredElytraHandler implements Listener abstract class SmithingTableListener extends ArmoredElytraHandler implements Listener
{ {
protected SmithingTableListener(ArmoredElytra plugin, boolean creationEnabled) protected SmithingTableListener(ArmoredElytra plugin, boolean creationEnabled,
INBTEditor nbtEditor, DurabilityManager durabilityManager, ConfigLoader config)
{ {
super(plugin, creationEnabled); super(plugin, creationEnabled, nbtEditor, durabilityManager, config);
}
protected SmithingTableListener(ArmoredElytra plugin)
{
this(plugin, false);
} }
public void onSmithingTableUsage(final PrepareSmithingEvent event) public void onSmithingTableUsage(final PrepareSmithingEvent event)
@ -42,17 +40,17 @@ abstract class SmithingTableListener extends ArmoredElytraHandler implements Lis
final Player player = (Player) event.getView().getPlayer(); final Player player = (Player) event.getView().getPlayer();
final ItemStack result;
if (plugin.playerHasCraftPerm(player, newTier)) if (plugin.playerHasCraftPerm(player, newTier))
{ {
final EnchantmentContainer enchantments = EnchantmentContainer.getEnchantments(itemStackA, plugin);
EnchantmentContainer enchantments = EnchantmentContainer.getEnchantments(itemStackA, plugin);
enchantments.merge(EnchantmentContainer.getEnchantments(itemStackB, plugin)); enchantments.merge(EnchantmentContainer.getEnchantments(itemStackB, plugin));
final Color color = getItemColor(itemStackA, itemStackB); final Color color = getItemColor(itemStackA, itemStackB);
result = ArmoredElytra.getInstance().getNbtEditor() final ItemStack result = nbtEditor.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), newTier,
.addArmorNBTTags(new ItemStack(Material.ELYTRA, 1), newTier, plugin.getConfigLoader().unbreakable(), color);
plugin.getConfigLoader().unbreakable(), color); durabilityManager.setCombinedDurability(result, itemStackA, itemStackB,
nbtEditor.getArmorTier(itemStackA), newTier);
enchantments.applyEnchantments(result); enchantments.applyEnchantments(result);
event.setResult(result); event.setResult(result);
} }
@ -82,7 +80,7 @@ abstract class SmithingTableListener extends ArmoredElytraHandler implements Lis
return false; return false;
// Check if the event was a player who interacted with a smithing table. // Check if the event was a player who interacted with a smithing table.
Player player = (Player) event.getWhoClicked(); final Player player = (Player) event.getWhoClicked();
if (event.getView().getType() != InventoryType.SMITHING) if (event.getView().getType() != InventoryType.SMITHING)
return false; return false;
@ -110,9 +108,8 @@ abstract class SmithingTableListener extends ArmoredElytraHandler implements Lis
final ItemStack result = smithingInventory.getItem(2); final ItemStack result = smithingInventory.getItem(2);
if (result == null || result.getType() != Material.ELYTRA || if (result == null || result.getType() != Material.ELYTRA ||
ArmoredElytra.getInstance().getNbtEditor().getArmorTier(result) == ArmorTier.NONE) nbtEditor.getArmorTier(result) == ArmorTier.NONE)
return false; return false;
return true; return true;
} }
} }

View File

@ -1,6 +1,7 @@
package nl.pim16aap2.armoredElytra.handlers; package nl.pim16aap2.armoredElytra.handlers;
import nl.pim16aap2.armoredElytra.ArmoredElytra; import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.nbtEditor.INBTEditor;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Material; import org.bukkit.Material;
@ -17,10 +18,12 @@ import org.bukkit.scheduler.BukkitRunnable;
public class Uninstaller implements Listener public class Uninstaller implements Listener
{ {
private final ArmoredElytra plugin; private final ArmoredElytra plugin;
private final INBTEditor nbtEditor;
public Uninstaller(ArmoredElytra plugin) public Uninstaller(ArmoredElytra plugin, INBTEditor nbtEditor)
{ {
this.plugin = plugin; this.plugin = plugin;
this.nbtEditor = nbtEditor;
} }
public int removeArmoredElytras(Inventory inv) public int removeArmoredElytras(Inventory inv)
@ -28,7 +31,7 @@ public class Uninstaller implements Listener
int count = 0; int count = 0;
for (ItemStack is : inv) for (ItemStack is : inv)
if (is != null && is.getType() == Material.ELYTRA && if (is != null && is.getType() == Material.ELYTRA &&
ArmoredElytra.getInstance().getNbtEditor().getArmorTier(is) != ArmorTier.NONE) nbtEditor.getArmorTier(is) != ArmorTier.NONE)
{ {
inv.remove(is); inv.remove(is);
++count; ++count;

View File

@ -4,6 +4,7 @@ import nl.pim16aap2.armoredElytra.ArmoredElytra;
import nl.pim16aap2.armoredElytra.util.ArmorTier; import nl.pim16aap2.armoredElytra.util.ArmorTier;
import org.bukkit.Color; import org.bukkit.Color;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List; import java.util.List;
@ -99,7 +100,7 @@ public interface INBTEditor
* @param item The item to check. * @param item The item to check.
* @return The {@link ArmorTier} that is on the item. If none is found, {@link ArmorTier#NONE} is returned. * @return The {@link ArmorTier} that is on the item. If none is found, {@link ArmorTier#NONE} is returned.
*/ */
ArmorTier getArmorTier(ItemStack item); ArmorTier getArmorTier(@Nullable ItemStack item);
/** /**
* Gets the Color of an armored elytra. * Gets the Color of an armored elytra.
@ -107,7 +108,26 @@ public interface INBTEditor
* If the provided {@link ItemStack} is not an AE, null is returned. * If the provided {@link ItemStack} is not an AE, null is returned.
* *
* @param item The armored elytra to check. * @param item The armored elytra to check.
* @return The color of the armored elytra, if the input is a color armored elytra, otherwise null. * @return The color of the armored elytra, if the input is a colored armored elytra, otherwise null.
*/ */
Color getColorOfArmoredElytra(ItemStack item); Color getColorOfArmoredElytra(@Nullable ItemStack item);
/**
* Updates the durability values of an item.
*
* @param itemStack The itemstack to which the durability values will be applied.
* @param realDurability The real durability to store in NBT.
* @param displayDurability The durability value to display on the item. This is the durability value the client can
* actually see.This only works if the item's meta is an instance of {@link Damageable}.
*/
void updateDurability(ItemStack itemStack, int realDurability, int displayDurability);
/**
* Gets the real durability value as stored in the NBT of an armored elytra.
*
* @param itemStack The item for which to retrieve the real durability.
* @param armorTier The armor tier of the armored elytra. If this is null, it will be retrieved from NBT.
* @return The real durability of the itemstack if the itemstack has the AE durability attribute, or -1 otherwise.
*/
int getRealDurability(ItemStack itemStack, @Nullable ArmorTier armorTier);
} }

View File

@ -10,6 +10,7 @@ import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeModifier; import org.bukkit.attribute.AttributeModifier;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
@ -17,14 +18,48 @@ import org.bukkit.persistence.PersistentDataType;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
public class NBTEditor implements INBTEditor public class NBTEditor implements INBTEditor
{ {
private static final NamespacedKey armorTierKey = new NamespacedKey(ArmoredElytra.getInstance(), private static final NamespacedKey ARMOR_TIER_KEY = new NamespacedKey(ArmoredElytra.getInstance(),
"ARMOR_TIER_LEVEL"); "ARMOR_TIER_LEVEL");
private static final NamespacedKey armorColorKey = new NamespacedKey(ArmoredElytra.getInstance(), private static final NamespacedKey ARMOR_COLOR_KEY = new NamespacedKey(ArmoredElytra.getInstance(),
"ARMORED_ELYTRA_COLOR"); "ARMORED_ELYTRA_COLOR");
private static final NamespacedKey DURABILITY_KEY = new NamespacedKey(ArmoredElytra.getInstance(),
"ARMORED_ELYTRA_DURABILITY");
@Override
public int getRealDurability(ItemStack itemStack, @Nullable ArmorTier providedTier)
{
final @Nullable ItemMeta meta = itemStack.getItemMeta();
final ArmorTier armorTier = providedTier == null ? getArmorTier(meta) : providedTier;
if (armorTier == ArmorTier.NONE)
return -1;
if (!(meta instanceof Damageable))
throw new IllegalStateException("Item \"" + itemStack + "\" with meta \"" + meta + "\" is not Damageable!");
final @Nullable Integer realDurability =
Objects.requireNonNull(meta, "Meta cannot be null for armored elytras!")
.getPersistentDataContainer().get(DURABILITY_KEY, PersistentDataType.INTEGER);
return realDurability == null ? -1 : realDurability;
}
@Override
public void updateDurability(ItemStack itemStack, int realDurability, int displayDurability)
{
final ItemMeta meta = getOrCreateItemMeta(itemStack);
meta.getPersistentDataContainer().set(DURABILITY_KEY, PersistentDataType.INTEGER, realDurability);
if (meta instanceof Damageable)
((Damageable) meta).setDamage(displayDurability);
itemStack.setItemMeta(meta);
}
@Override @Override
public ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable, String name, public ItemStack addArmorNBTTags(ItemStack item, ArmorTier armorTier, boolean unbreakable, String name,
@ -33,14 +68,13 @@ public class NBTEditor implements INBTEditor
if (armorTier == null || armorTier == ArmorTier.NONE) if (armorTier == null || armorTier == ArmorTier.NONE)
return new ItemStack(item); return new ItemStack(item);
ItemStack ret = new ItemStack(item); final ItemStack ret = new ItemStack(item);
ItemMeta meta = ret.hasItemMeta() ? ret.getItemMeta() : Bukkit.getItemFactory().getItemMeta(ret.getType()); final ItemMeta meta = getOrCreateItemMeta(ret);
if (meta == null) meta.getPersistentDataContainer().set(ARMOR_TIER_KEY, PersistentDataType.INTEGER,
throw new IllegalArgumentException("Tried to add armor to invalid item: " + item); ArmorTier.getTierID(armorTier));
meta.getPersistentDataContainer().set(armorTierKey, PersistentDataType.INTEGER, ArmorTier.getTierID(armorTier));
if (color != null && armorTier == ArmorTier.LEATHER) if (color != null && armorTier == ArmorTier.LEATHER)
meta.getPersistentDataContainer().set(armorColorKey, PersistentDataType.INTEGER, color.asRGB()); meta.getPersistentDataContainer().set(ARMOR_COLOR_KEY, PersistentDataType.INTEGER, color.asRGB());
overwriteNBTValue(meta, Attribute.GENERIC_ARMOR, ArmorTier.getArmor(armorTier), "generic.armor"); overwriteNBTValue(meta, Attribute.GENERIC_ARMOR, ArmorTier.getArmor(armorTier), "generic.armor");
if (ArmorTier.getToughness(armorTier) > 0) if (ArmorTier.getToughness(armorTier) > 0)
@ -65,33 +99,29 @@ public class NBTEditor implements INBTEditor
if (meta.hasAttributeModifiers()) if (meta.hasAttributeModifiers())
meta.removeAttributeModifier(attribute); meta.removeAttributeModifier(attribute);
AttributeModifier attributeModifier = new AttributeModifier(UUID.randomUUID(), modifierName, value, final AttributeModifier attributeModifier = new AttributeModifier(UUID.randomUUID(), modifierName, value,
AttributeModifier.Operation.ADD_NUMBER, AttributeModifier.Operation.ADD_NUMBER,
EquipmentSlot.CHEST); EquipmentSlot.CHEST);
meta.addAttributeModifier(attribute, attributeModifier); meta.addAttributeModifier(attribute, attributeModifier);
} }
@Override private ArmorTier getArmorTier(@Nullable ItemMeta meta)
public ArmorTier getArmorTier(ItemStack item)
{ {
if (item == null)
return ArmorTier.NONE;
ItemMeta meta = item.getItemMeta();
if (meta == null || !meta.hasAttributeModifiers()) if (meta == null || !meta.hasAttributeModifiers())
return ArmorTier.NONE; return ArmorTier.NONE;
Integer tierID = meta.getPersistentDataContainer().get(armorTierKey, PersistentDataType.INTEGER); final @Nullable Integer tierID = meta.getPersistentDataContainer()
.get(ARMOR_TIER_KEY, PersistentDataType.INTEGER);
if (tierID != null) if (tierID != null)
return ArmorTier.getArmorTierFromID(tierID); return ArmorTier.getArmorTierFromID(tierID);
Collection<AttributeModifier> attributeModifiers = meta.getAttributeModifiers(Attribute.GENERIC_ARMOR); final Collection<AttributeModifier> attributeModifiers = meta.getAttributeModifiers(Attribute.GENERIC_ARMOR);
if (attributeModifiers == null) if (attributeModifiers == null)
return ArmorTier.NONE; return ArmorTier.NONE;
for (final AttributeModifier attributeModifier : attributeModifiers) for (final AttributeModifier attributeModifier : attributeModifiers)
{ {
ArmorTier armorTier = ArmorTier.getArmorTierFromArmor((int) attributeModifier.getAmount()); final ArmorTier armorTier = ArmorTier.getArmorTierFromArmor((int) attributeModifier.getAmount());
if (armorTier != ArmorTier.NONE) if (armorTier != ArmorTier.NONE)
return armorTier; return armorTier;
} }
@ -100,7 +130,15 @@ public class NBTEditor implements INBTEditor
} }
@Override @Override
public Color getColorOfArmoredElytra(final ItemStack item) public ArmorTier getArmorTier(@Nullable ItemStack item)
{
if (item == null)
return ArmorTier.NONE;
return getArmorTier(item.getItemMeta());
}
@Override
public Color getColorOfArmoredElytra(@Nullable ItemStack item)
{ {
if (item == null || item.getType() != Material.ELYTRA || !item.hasItemMeta()) if (item == null || item.getType() != Material.ELYTRA || !item.hasItemMeta())
return null; return null;
@ -110,10 +148,20 @@ public class NBTEditor implements INBTEditor
return null; return null;
final PersistentDataContainer container = meta.getPersistentDataContainer(); final PersistentDataContainer container = meta.getPersistentDataContainer();
if (!container.has(armorColorKey, PersistentDataType.INTEGER)) if (!container.has(ARMOR_COLOR_KEY, PersistentDataType.INTEGER))
return null; return null;
final Integer rgb = container.get(armorColorKey, PersistentDataType.INTEGER); final Integer rgb = container.get(ARMOR_COLOR_KEY, PersistentDataType.INTEGER);
return rgb == null ? null : Color.fromRGB(rgb); return rgb == null ? null : Color.fromRGB(rgb);
} }
private static ItemMeta getOrCreateItemMeta(ItemStack item)
{
final ItemMeta meta = item.hasItemMeta() ?
item.getItemMeta() :
Bukkit.getItemFactory().getItemMeta(item.getType());
if (meta == null)
throw new IllegalArgumentException("Tried to add armor to invalid item: " + item);
return meta;
}
} }

View File

@ -2,5 +2,33 @@ package nl.pim16aap2.armoredElytra.util;
public enum Action public enum Action
{ {
NONE, REPAIR, ENCHANT, COMBINE, CREATE, BLOCK /**
* Take no action at all and let vanilla (or some other plugin) handle the process.
*/
NONE,
/**
* Repair an armored elytra.
*/
REPAIR,
/**
* Enchant an armored elytra.
*/
ENCHANT,
/**
* Combines one armored elytra with another one of the same tier.
*/
COMBINE,
/**
* Creates a new armored elytra.
*/
CREATE,
/**
* Blocks an otherwise valid input.
*/
BLOCK
} }

View File

@ -7,14 +7,14 @@ import java.util.Map;
public enum ArmorTier public enum ArmorTier
{ {
// Tier: TierID, armor-value, armor-toughness, knockbackResistance, repair // Tier: TierID, armor-value, armor-toughness, knockbackResistance, repair, defaultRepairCount, name, durability
NONE(0, 0, 0, 0, null, ""), NONE(0, 0, 0, 0, null, 0, "", 0),
LEATHER(1, 3, 0, 0, Material.LEATHER, "leather"), LEATHER(1, 3, 0, 0, Material.LEATHER, 6, "leather", 80),
GOLD(2, 5, 0, 0, Material.GOLD_INGOT, "gold"), GOLD(2, 5, 0, 0, Material.GOLD_INGOT, 6, "gold", 112),
CHAIN(3, 5, 0, 0, Material.IRON_INGOT, "chain"), CHAIN(3, 5, 0, 0, Material.IRON_INGOT, 4, "chain", 240),
IRON(4, 6, 0, 0, Material.IRON_INGOT, "iron"), IRON(4, 6, 0, 0, Material.IRON_INGOT, 4, "iron", 240),
DIAMOND(5, 8, 2, 0, Material.DIAMOND, "diamond"), DIAMOND(5, 8, 2, 0, Material.DIAMOND, 3, "diamond", 528),
NETHERITE(6, 8, 3, 0.1, XMaterial.NETHERITE_INGOT.parseMaterial(), "netherite"), NETHERITE(6, 8, 3, 0.1, XMaterial.NETHERITE_INGOT.parseMaterial(), 3, "netherite", 592),
; ;
private final int tierID; private final int tierID;
@ -22,34 +22,41 @@ public enum ArmorTier
private final int toughness; private final int toughness;
private final double knockbackResistance; private final double knockbackResistance;
private final Material repair; private final Material repair;
private final int defaultRepairCount;
private final String name; private final String name;
private final int durability;
private static final Map<String, ArmorTier> map = new HashMap<>(); private static final Map<String, ArmorTier> map = new HashMap<>();
private static final Map<Integer, ArmorTier> armorValueMap = new HashMap<>(); private static final Map<Integer, ArmorTier> armorValueMap = new HashMap<>();
private static final Map<Integer, ArmorTier> armorIDMap = new HashMap<>(); private static final Map<Integer, ArmorTier> armorIDMap = new HashMap<>();
ArmorTier(int tierID, int armor, int toughness, double knockbackResistance, Material repair, String name) ArmorTier(int tierID, int armor, int toughness, double knockbackResistance, Material repair,
int defaultRepairCount, String name, int durability)
{ {
this.tierID = tierID; this.tierID = tierID;
this.armor = armor; this.armor = armor;
this.toughness = toughness; this.toughness = toughness;
this.knockbackResistance = knockbackResistance; this.knockbackResistance = knockbackResistance;
this.repair = repair; this.repair = repair;
this.defaultRepairCount = defaultRepairCount;
this.name = name; this.name = name;
this.durability = durability;
} }
// return the armor value of a tier.
public static int getArmor(ArmorTier tier) public static int getArmor(ArmorTier tier)
{ {
return tier.armor; return tier.armor;
} }
// return the armor value of a tier. public static int getMaxDurability(ArmorTier tier)
{
return tier.durability;
}
public static int getTierID(ArmorTier tier) public static int getTierID(ArmorTier tier)
{ {
return tier.tierID; return tier.tierID;
} }
// return the armor toughness of a tier.
public static int getToughness(ArmorTier tier) public static int getToughness(ArmorTier tier)
{ {
return tier.toughness; return tier.toughness;
@ -105,4 +112,9 @@ public enum ArmorTier
// be around from when it was still used. // be around from when it was still used.
armorValueMap.put(ArmorTier.DIAMOND.armor, ArmorTier.DIAMOND); armorValueMap.put(ArmorTier.DIAMOND.armor, ArmorTier.DIAMOND);
} }
public static int getDefaultRepairCount(ArmorTier armorTier)
{
return armorTier.defaultRepairCount;
}
} }

View File

@ -6,6 +6,7 @@ import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
@ -23,14 +24,18 @@ public class ConfigLoader
private boolean unbreakable; private boolean unbreakable;
private boolean enableDebug; private boolean enableDebug;
private String languageFile; private String languageFile;
private int GOLD_TO_FULL;
private int IRON_TO_FULL; private final int[] repairCounts = new int[ArmorTier.values().length];
private int goldToFull;
private int ironToFull;
private int leatherToFull;
private int diamondsToFull;
private int netheriteToFull;
private boolean uninstallMode; private boolean uninstallMode;
private boolean checkForUpdates; private boolean checkForUpdates;
private int LEATHER_TO_FULL;
private int DIAMONDS_TO_FULL;
private int NETHERITE_TO_FULL;
private boolean noFlightDurability; private boolean noFlightDurability;
private boolean useTierDurability;
private boolean dropNetheriteAsChestplate; private boolean dropNetheriteAsChestplate;
private LinkedHashSet<Enchantment> allowedEnchantments; private LinkedHashSet<Enchantment> allowedEnchantments;
private boolean allowMultipleProtectionEnchantments; private boolean allowMultipleProtectionEnchantments;
@ -66,10 +71,17 @@ public class ConfigLoader
"Setting this to true will cause armored elytras to not lose any durability while flying.", "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." "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 = String[] repairComment =
{ {
"Amount of items it takes to fully repair an armored elytra", "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%." "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 = String[] enchantmentsComment =
{ {
@ -151,7 +163,6 @@ public class ConfigLoader
"When true, only enchantments from the allowed list can be added." "When true, only enchantments from the allowed list can be added."
}; };
// Set default list of allowed enchantments. // Set default list of allowed enchantments.
List<String> defaultAllowedEnchantments = new ArrayList<>( List<String> defaultAllowedEnchantments = new ArrayList<>(
Arrays.asList("minecraft:unbreaking", "minecraft:fire_protection", "minecraft:blast_protection", Arrays.asList("minecraft:unbreaking", "minecraft:fire_protection", "minecraft:blast_protection",
@ -161,13 +172,29 @@ public class ConfigLoader
FileConfiguration config = plugin.getConfig(); FileConfiguration config = plugin.getConfig();
unbreakable = addNewConfigOption(config, "unbreakable", false, unbreakableComment); unbreakable = addNewConfigOption(config, "unbreakable", false, unbreakableComment);
noFlightDurability = addNewConfigOption(config, "noFlightDurability", false, flyDurabilityComment); noFlightDurability = addNewConfigOption(config, "noFlightDurability", false, flyDurabilityComment);
LEATHER_TO_FULL = addNewConfigOption(config, "leatherRepair", 6, repairComment); useTierDurability = addNewConfigOption(config, "useTierDurability", true, useTierDurabilityComment);
GOLD_TO_FULL = addNewConfigOption(config, "goldRepair", 5, null);
IRON_TO_FULL = addNewConfigOption(config, "ironRepair", 4, null); final ArmorTier[] armorTiers = ArmorTier.values();
DIAMONDS_TO_FULL = addNewConfigOption(config, "diamondsRepair", 3, null); for (int idx = 1; idx < armorTiers.length; ++idx)
NETHERITE_TO_FULL = addNewConfigOption(config, "netheriteIngotsRepair", 3, null); {
final ArmorTier armorTier = armorTiers[idx];
// Only the first one should have the comment.
final @Nullable String[] comment = idx == 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);
}
final int armorTierCount = ArmorTier.values().length;
if (repairCounts.length != armorTierCount)
throw new IllegalStateException("Incorrect repair counts array size! Expected size " +
armorTierCount + " but got size " + repairCounts.length);
final boolean smithingTableAllowed = plugin.getMinecraftVersion().isNewerThan(MinecraftVersion.v1_15); final boolean smithingTableAllowed = plugin.getMinecraftVersion().isNewerThan(MinecraftVersion.v1_15);
craftingInSmithingTable = addNewConfigOption(config, "craftingInSmithingTable", smithingTableAllowed, craftingInSmithingTable = addNewConfigOption(config, "craftingInSmithingTable", smithingTableAllowed,
@ -308,29 +335,9 @@ public class ConfigLoader
return languageFile; return languageFile;
} }
public int LEATHER_TO_FULL() public int getFullRepairItemCount(ArmorTier armorTier)
{ {
return LEATHER_TO_FULL; return repairCounts[armorTier.ordinal()];
}
public int GOLD_TO_FULL()
{
return GOLD_TO_FULL;
}
public int IRON_TO_FULL()
{
return IRON_TO_FULL;
}
public int DIAMONDS_TO_FULL()
{
return DIAMONDS_TO_FULL;
}
public int NETHERITE_TO_FULL()
{
return NETHERITE_TO_FULL;
} }
public boolean allowMultipleProtectionEnchantments() public boolean allowMultipleProtectionEnchantments()
@ -382,4 +389,9 @@ public class ConfigLoader
{ {
return bypassCraftPerm; return bypassCraftPerm;
} }
public boolean useTierDurability()
{
return useTierDurability;
}
} }

View File

@ -2,10 +2,14 @@ package nl.pim16aap2.armoredElytra.util;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import javax.annotation.Nullable;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Locale;
import java.util.Map; import java.util.Map;
public class Util public class Util
@ -85,6 +89,41 @@ public class Util
} }
} }
public static String snakeToCamelCase(String input)
{
final char[] arr = input.toLowerCase(Locale.US).toCharArray();
int skipped = 0;
boolean capitalize = false;
for (int idx = 0; idx < arr.length; ++idx)
{
char current = arr[idx];
if (current == '_')
{
++skipped;
capitalize = true;
continue;
}
final int targetIdx = idx - skipped;
if (capitalize)
{
if (targetIdx > 0)
current = Character.toUpperCase(current);
capitalize = false;
}
// targetIdx is always <= idx, so we can reuse the current array
// without overwriting any values we will need in the future.
arr[targetIdx] = current;
}
return new String(arr, 0, arr.length - skipped);
}
// Function that returns which/how many protection enchantments there are. // Function that returns which/how many protection enchantments there are.
public static int getProtectionEnchantmentsVal(Map<Enchantment, Integer> enchantments) public static int getProtectionEnchantmentsVal(Map<Enchantment, Integer> enchantments)
{ {
@ -101,4 +140,29 @@ public class Util
ret += 16; ret += 16;
return ret; return ret;
} }
public static void moveChestplateToInventory(Player player)
{
final PlayerInventory inventory = player.getInventory();
inventory.addItem(inventory.getChestplate());
final @Nullable ItemStack chestplate = inventory.getChestplate();
if (chestplate != null)
chestplate.setAmount(0);
player.updateInventory();
}
/**
* Ensures that a given value does not exceed the provided upper and lower bounds.
*
* @param val The value to check.
* @param min The lower bound limit.
* @param max The upper bound limit.
* @return The value if it is bigger than min and larger than max, otherwise either min or max.
*/
public static int between(int val, int min, int max)
{
return Math.max(min, Math.min(max, val));
}
} }

View File

@ -0,0 +1,18 @@
package nl.pim16aap2.armoredElytra.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class UtilTest
{
@Test
void testSnakeToCamelCase()
{
Assertions.assertEquals("testCase", Util.snakeToCamelCase("TeSt_Case"));
Assertions.assertEquals("testCase", Util.snakeToCamelCase("____test_case"));
Assertions.assertEquals("", Util.snakeToCamelCase("________"));
Assertions.assertEquals("testCase", Util.snakeToCamelCase("TeSt__Case____"));
Assertions.assertEquals("t", Util.snakeToCamelCase("_T_"));
Assertions.assertEquals("testcase", Util.snakeToCamelCase("TeStCase"));
}
}