package net.knarcraft.blacksmith.trait; import net.citizensnpcs.api.npc.NPC; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.config.scrapper.ScrapperNPCSettings; import net.knarcraft.blacksmith.event.ScrapperSalvageFailEvent; import net.knarcraft.blacksmith.event.ScrapperSalvageSucceedEvent; import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.property.SalvageMethod; import net.knarcraft.blacksmith.util.ItemHelper; import net.knarcraft.blacksmith.util.SalvageHelper; import org.bukkit.Material; import org.bukkit.Sound; 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 static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage; /** * A representation of the session between a player and a scrapper */ public class SalvageSession extends Session implements Runnable { private final ScrapperTrait scrapperTrait; private final ItemStack itemToSalvage; private final ScrapperNPCSettings config; private final List salvage; private final int itemsConsumed; private final int enchantmentLevels; protected final SalvageMethod salvageMethod; private static final Random random = new Random(); /** * Instantiates a new session * * @param scrapperTrait

A reference to the scrapper trait

* @param player

The player initiating the session

* @param npc

The scrapper NPC involved in the session

* @param config

The config to use for the session

* @param salvageMethod

The salvage method performed in this session

* @param itemsConsumed

The number of items actually consumed, in case a salvaging fails

*/ public SalvageSession(@NotNull ScrapperTrait scrapperTrait, @NotNull Player player, @NotNull NPC npc, @NotNull ScrapperNPCSettings config, @NotNull List salvage, @NotNull SalvageMethod salvageMethod, int itemsConsumed) { super(player, npc); this.scrapperTrait = scrapperTrait; this.itemToSalvage = player.getInventory().getItemInMainHand().clone(); this.config = config; this.salvage = salvage; this.enchantmentLevels = SalvageHelper.getTotalEnchantmentLevels(this.itemToSalvage); this.salvageMethod = salvageMethod; this.itemsConsumed = itemsConsumed; } @Override public boolean isSessionInvalid() { // Prevent player from switching items during session ItemStack itemInHand = this.player.getInventory().getItemInMainHand(); if (!itemToSalvage.equals(itemInHand)) { sendNPCMessage(this.npc, this.player, this.config.getItemChangedMessage()); return true; } if (EconomyManager.cannotPayForSalvage(this.player, this.salvageMethod)) { sendNPCMessage(this.npc, this.player, this.config.getInsufficientFundsMessage()); return true; } return false; } @Override protected int getActionDelay() { if (this.config.getMaxSalvageDelay() > 0) { //Finish the salvaging after a random delay between the max and min return new Random().nextInt(this.config.getMaxSalvageDelay()) + this.config.getMinSalvageDelay(); } else { //Finish the salvaging as soon as possible return 0; } } @Override protected @NotNull Material getCraftingStation() { return switch (this.salvageMethod) { case EXTENDED_SALVAGE -> Material.CRAFTING_TABLE; case SALVAGE -> Material.ANVIL; case NETHERITE, ARMOR_TRIM -> Material.SMITHING_TABLE; }; } /** * Runs the actual salvage which fixes the item and gives it back to the player */ @Override public void run() { salvageItem(); //Stop the reforged item from displaying in the scrapper'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); } //Mark this scrapper as available this.scrapperTrait.unsetSession(); // Start cool-down Calendar wait = Calendar.getInstance(); wait.add(Calendar.SECOND, this.config.getSalvageCoolDown()); this.scrapperTrait.addCoolDown(this.player.getUniqueId(), wait); } /** * Gets the number of items consumed in order to perform the salvage * * @return

The number of items consumed as part of this salvage

*/ public int getItemsConsumed() { return this.itemsConsumed; } /** * Trues to salvage an item, and gives the return item */ private void salvageItem() { if (random.nextInt(100) < this.config.getFailChance()) { playSound(Sound.ENTITY_VILLAGER_NO); failSalvage(); BlacksmithPlugin.getInstance().callEvent(new ScrapperSalvageFailEvent(this.npc, this.player)); } else { playSound(Sound.BLOCK_ANVIL_USE); giveSalvage(); BlacksmithPlugin.getInstance().callEvent(new ScrapperSalvageSucceedEvent(this.npc, this.player)); sendNPCMessage(this.npc, this.player, this.config.getSuccessMessage()); } } /** * The method to run when a crapper fails salvaging an item */ private void failSalvage() { // Make sure the given amount is the same amount taken for salvage to avoid duplication this.itemToSalvage.setAmount(itemsConsumed); if (ItemHelper.getMaxDurability(this.itemToSalvage) > 0) { //Damage the item if possible damageItemRandomly(this.itemToSalvage); giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), this.npc, this.itemToSalvage); sendNPCMessage(this.npc, this.player, this.config.getFailSalvageMessage()); } else { // Give half the salvage List halfSalvage = SalvageHelper.pickRandomSalvage(this.salvage, new ArrayList<>(), 0.5); for (ItemStack item : halfSalvage) { giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), this.npc, item); } sendNPCMessage(this.npc, this.player, this.config.getFailExtendedSalvageMessage()); } } /** * Gives the player the calculated salvage */ private void giveSalvage() { // TODO: Find a better calculation than 1 enchantment level = 1 exp level // Gives the player back some of the EXP used on an item this.player.giveExpLevels(this.enchantmentLevels); BlacksmithPlugin.debug("Giving salvage " + this.salvage); for (ItemStack item : this.salvage) { giveResultingItem(this.config.getMaxSalvageDelay() > 0, this.config.getDropItem(), this.npc, item); } } }