237 lines
9.3 KiB
Java

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.event.BlacksmithReforgeFailEvent;
import net.knarcraft.blacksmith.event.BlacksmithReforgeSucceedEvent;
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<String> enchantments = null;
/**
* Instantiates a new session
*
* @param blacksmithTrait <p>A reference to the blacksmith trait</p>
* @param player <p>The player initiating the session</p>
* @param npc <p>The Blacksmith NPC involved in the session</p>
* @param config <p>The config to use for the session</p>
*/
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<Enchantment> 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 <p>Whether the reforge was successful. False if the blacksmith failed to fully repair the item.</p>
*/
private boolean reforgeItem() {
if (random.nextInt(100) < this.config.getFailChance()) {
failReforge();
BlacksmithPlugin.getInstance().callEvent(new BlacksmithReforgeFailEvent(this.npc, this.player));
return false;
} else {
succeedReforge();
BlacksmithPlugin.getInstance().callEvent(new BlacksmithReforgeSucceedEvent(this.npc, this.player));
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<Enchantment> 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);
}
}
}
}