package net.knarcraft.blacksmith.trait; import net.citizensnpcs.api.npc.NPC; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.config.blacksmith.BlacksmithNPCSettings; import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.util.InputParsingHelper; import net.knarcraft.blacksmith.util.ItemHelper; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Registry; import org.bukkit.Sound; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.logging.Level; import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage; /** * A representation of the session between a player and a blacksmith */ public class ReforgeSession extends Session implements Runnable { private final BlacksmithTrait blacksmithTrait; private final ItemStack itemToReforge; private final BlacksmithNPCSettings config; private static List enchantments = null; /** * Instantiates a new session * * @param blacksmithTrait

A reference to the blacksmith trait

* @param player

The player initiating the session

* @param npc

The Blacksmith NPC involved in the session

* @param config

The config to use for the session

*/ ReforgeSession(@NotNull BlacksmithTrait blacksmithTrait, @NotNull Player player, @NotNull NPC npc, @NotNull BlacksmithNPCSettings config) { super(player, npc); this.blacksmithTrait = blacksmithTrait; this.itemToReforge = player.getInventory().getItemInMainHand(); this.config = config; //Populate enchantments the first time this is run if (enchantments == null) { Registry enchantmentRegistry = Bukkit.getRegistry(Enchantment.class); if (enchantmentRegistry == null) { throw new RuntimeException("Unable to get enchantment registry"); } enchantments = new ArrayList<>(); for (Enchantment enchantment : enchantmentRegistry) { enchantments.add(enchantment.getKey().getKey()); } } } @Override public boolean isSessionInvalid() { // Prevent player from switching items during session ItemStack itemInHand = this.player.getInventory().getItemInMainHand(); if (!itemToReforge.equals(itemInHand)) { sendNPCMessage(this.npc, this.player, this.config.getItemChangedMessage()); return true; } // The player is unable to pay if (EconomyManager.cannotPayForHeldItemReforge(this.player)) { sendNPCMessage(this.npc, this.player, this.config.getInsufficientFundsMessage()); return true; } return false; } @Override protected int getActionDelay() { if (this.config.getMaxReforgeDelay() > 0) { //Finish the reforging after a random delay between the max and min return new Random().nextInt(this.config.getMaxReforgeDelay()) + this.config.getMinReforgeDelay(); } else { //Finish the salvaging as soon as possible return 0; } } /** * Runs the actual reforge which fixes the item and gives it back to the player */ @Override public void run() { boolean success = reforgeItem(); sendNPCMessage(this.npc, this.player, success ? this.config.getSuccessMessage() : this.config.getFailMessage()); playSound(success ? Sound.BLOCK_ANVIL_USE : Sound.ENTITY_VILLAGER_NO); //Stop the reforged item from displaying in the blacksmith's hand if (this.npc.getEntity() instanceof Player) { ((Player) this.npc.getEntity()).getInventory().setItemInMainHand(null); } else { Objects.requireNonNull(((LivingEntity) this.npc.getEntity()).getEquipment()).setItemInMainHand(null); } //Give the item back to the player giveReforgedItem(); //Mark this blacksmith as available this.blacksmithTrait.unsetSession(); // Start cool-down Calendar wait = Calendar.getInstance(); wait.add(Calendar.SECOND, this.config.getReforgeCoolDown()); this.blacksmithTrait.addCoolDown(this.player.getUniqueId(), wait); } /** * Gives the reforged item back to the player */ private void giveReforgedItem() { giveResultingItem(this.config.getMaxReforgeDelay() > 0, this.config.getDropItem(), this.npc, this.itemToReforge); } /** * Performs the actual reforge where the item's damage is reduced * * @return

Whether the reforge was successful. False if the blacksmith failed to fully repair the item.

*/ private boolean reforgeItem() { if (random.nextInt(100) < this.config.getFailChance()) { failReforge(); return false; } else { succeedReforge(); return true; } } /** * The method to run when a blacksmith successfully reforges an item */ private void succeedReforge() { // Remove any damage done to the item if (ItemHelper.updateDamage(this.itemToReforge, 0)) { BlacksmithPlugin.getInstance().getLogger().log(Level.WARNING, "Unable to update damage for " + this.itemToReforge); } //Replace damaged anvils with a normal anvil if (ItemHelper.isAnvil(this.itemToReforge.getType(), true)) { this.itemToReforge.setType(Material.ANVIL); } //See if a random roll (0-99) is less than extraEnchantmentChance, and add a random enchantment int roll = random.nextInt(100); if (roll < this.config.getExtraEnchantmentChance() && this.itemToReforge.getEnchantments().keySet().size() < this.config.getMaxEnchantments() && !ItemHelper.isAnvil(this.itemToReforge.getType(), false)) { addRandomEnchantment(); } } /** * Adds a random enchantment to the currently reforged item */ private void addRandomEnchantment() { //Find usable enchantments first List usableEnchantments = new ArrayList<>(); for (String enchantmentName : enchantments) { Enchantment enchantment = InputParsingHelper.matchEnchantment(enchantmentName); if (enchantment != null && enchantment.canEnchantItem(this.itemToReforge)) { usableEnchantments.add(enchantment); } } //Remove any enchantments in the block list usableEnchantments.removeAll(this.blacksmithTrait.getSettings().getEnchantmentBlockList()); //In case all usable enchantments have been blocked, abort if (usableEnchantments.isEmpty()) { return; } //Choose a random enchantment Enchantment randomEnchantment = usableEnchantments.get(random.nextInt(usableEnchantments.size())); if (randomEnchantment != null) { int randomBound = randomEnchantment.getMaxLevel() + 1; //A workaround for the random method's bound sometimes being negative if (randomBound >= 0) { int existingLevel = this.itemToReforge.getEnchantmentLevel(randomEnchantment); /* Add a random enchantment whose level is no lower than the start level, and no lower than the existing level (to prevent making the item worse) */ this.itemToReforge.addEnchantment(randomEnchantment, Math.max(Math.max(random.nextInt(randomBound), randomEnchantment.getStartLevel()), existingLevel)); } } } /** * The method to run when a blacksmith fails re-forging an item */ private void failReforge() { if (this.config.getFailRemovesEnchantments()) { removeOrDowngradeEnchantments(); } //Damage the item damageItemRandomly(this.itemToReforge); } /** * Removes or downgrades all enchantments for the currently reforged item */ private void removeOrDowngradeEnchantments() { //Remove or downgrade existing enchantments for (Enchantment enchantment : this.itemToReforge.getEnchantments().keySet()) { //Completely remove the enchantment, downgrade it, or keep it if lucky and already level 1 if (random.nextBoolean()) { this.itemToReforge.removeEnchantment(enchantment); } else if (this.itemToReforge.getEnchantmentLevel(enchantment) > 1) { this.itemToReforge.removeEnchantment(enchantment); this.itemToReforge.addEnchantment(enchantment, 1); } } } }