package net.knarcraft.blacksmith.trait; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.config.NPCSettings; import net.knarcraft.blacksmith.formatting.TimeFormatter; import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.blacksmith.util.ItemHelper; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder; import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage; /** * The class representing the Blacksmith NPC trait */ public class BlacksmithTrait extends Trait { private final Map coolDowns = new HashMap<>(); private ReforgeSession session; private final NPCSettings config; private long _sessionStart = System.currentTimeMillis(); /** * Instantiates a new blacksmith trait */ public BlacksmithTrait() { super("blacksmith"); //This should crash if the blacksmith plugin hasn't been properly registered Bukkit.getServer().getPluginManager().getPlugin("Blacksmith"); this.config = new NPCSettings(BlacksmithPlugin.getInstance().getSettings()); } /** * Gets the current settings for this NPC * * @return

The current settings for this NPC

*/ public NPCSettings getSettings() { return config; } /** * Gets whether this blacksmith is already in a reforging session * * @return

Whether already in a reforge session

*/ public boolean hasSession() { return this.session != null; } /** * Adds a cool-down for the given player's next blacksmith reforge * * @param playerUUID

The ID of the player to add the cool-down for

* @param waitUntil

The time when the player can interact again

*/ public void addCoolDown(UUID playerUUID, Calendar waitUntil) { coolDowns.put(playerUUID, waitUntil); } /** * Unsets the session of this blacksmith, making it ready for another round */ public void unsetSession() { this.session = null; } /** * Loads all config values stored in citizens' config file for this NPC * * @param key

The data key used for the config root

*/ @Override public void load(DataKey key) { config.loadVariables(key); } /** * Saves all config values for this NPC * * @param key

The data key used for the config root

*/ @Override public void save(DataKey key) { config.saveVariables(key); } /** * Performs necessary work before the session is started or continued * * @param player

The player to prepare the session for

* @return

True if preparations were successful. False if a session shouldn't be started

*/ public boolean prepareForSession(Player player) { UUID playerId = player.getUniqueId(); //If cool-down has been disabled after it was set for this player, remove the cool-down if (config.getDisableCoolDown() && coolDowns.get(playerId) != null) { coolDowns.remove(playerId); } //Deny if permission is missing if (!player.hasPermission("blacksmith.reforge")) { return false; } //Deny if on cool-down, or remove cool-down if expired if (coolDowns.get(playerId) != null) { Calendar calendar = Calendar.getInstance(); if (!calendar.after(coolDowns.get(playerId))) { int secondDifference = (int) ((calendar.getTimeInMillis() - coolDowns.get(playerId).getTimeInMillis()) * 1000); boolean exactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime(); sendNPCMessage(this.npc, player, replacePlaceholder(config.getCoolDownUnexpiredMessage(), "{time}", TimeFormatter.formatTime(exactTime, secondDifference))); return false; } coolDowns.remove(playerId); } //If already in a session, but the player has failed to interact, and left the blacksmith, allow a new session if (session != null) { if (!session.isRunning() && (System.currentTimeMillis() > _sessionStart + 10 * 1000 || this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20)) { session = null; } } return true; } /** * Tries to continue the session for the given player * * @param player

The player to continue the session for

*/ public void continueSession(Player player) { //Another player is using the blacksmith if (!session.isInSession(player)) { sendNPCMessage(this.npc, player, config.getBusyWithPlayerMessage()); return; } //The blacksmith is already reforging for the player if (session.isRunning()) { int timeRemaining = (int) ((session.getFinishTime() - System.currentTimeMillis()) / 1000); boolean showExactTime = BlacksmithPlugin.getInstance().getSettings().getShowExactTime(); sendNPCMessage(this.npc, player, replacePlaceholder(config.getBusyReforgingMessage(), "{time}", TimeFormatter.formatTime(showExactTime, timeRemaining))); return; } if (session.endSession()) { //Quit if the player cannot afford, or has changed their item session = null; } else { //Start reforging for the player reforge(npc, player); } } /** * Starts a new session, and prepares to repair the player's item * * @param player

The player to start the session for

*/ public void startSession(Player player) { ItemStack hand = player.getInventory().getItemInMainHand(); //Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item List reforgeAbleItems = config.getReforgeAbleItems(); if (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) { String invalidMessage = replacePlaceholder(config.getInvalidItemMessage(), "{title}", config.getBlacksmithTitle()); sendNPCMessage(this.npc, player, invalidMessage); return; } if (ItemHelper.getDamage(hand) == 0) { sendNPCMessage(this.npc, player, config.getNotDamagedMessage()); return; } //Start a new reforge session for the player _sessionStart = System.currentTimeMillis(); session = new ReforgeSession(this, player, npc, config); //Tell the player the cost of repairing the item String cost = EconomyManager.formatCost(player); String itemName = hand.getType().name().toLowerCase().replace('_', ' '); sendNPCMessage(this.npc, player, config.getCostMessage().replace("{cost}", cost).replace("{item}", itemName)); } /** * Starts reforging the player's item * * @param npc

The NPC performing the reforge

* @param player

The player that initiated the reforge

*/ private void reforge(NPC npc, Player player) { sendNPCMessage(this.npc, player, config.getStartReforgeMessage()); EconomyManager.withdraw(player); session.beginReforge(); ItemStack heldItem = player.getInventory().getItemInMainHand(); //Display the item in the NPC's hand if (npc.getEntity() instanceof Player) { ((Player) npc.getEntity()).getInventory().setItemInMainHand(heldItem); } else { Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(heldItem); } //Remove the item from the player's inventory player.getInventory().setItemInMainHand(null); } /** * Gets whether the given item is repairable * * @param item

The item to check

* @return

True if the item is repairable

*/ public static boolean isRepairable(ItemStack item) { return item.getItemMeta() instanceof Damageable && EnchantmentTarget.BREAKABLE.includes(item); } }