package net.knarcraft.blacksmith.trait; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.trait.Trait; import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.config.Setting; import net.knarcraft.blacksmith.config.Settings; import net.knarcraft.blacksmith.config.TraitSettings; import net.knarcraft.blacksmith.formatting.TimeFormatter; import net.knarcraft.blacksmith.manager.EconomyManager; import net.knarcraft.knarlib.formatting.StringFormatter; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.UUID; import static net.knarcraft.blacksmith.formatting.BlacksmithStringFormatter.sendNPCMessage; /** * A custom trait that utilizes sessions */ public abstract class CustomTrait extends Trait { protected Session session; protected TraitSettings config; protected final Map coolDowns = new HashMap<>(); protected long currentSessionStartTime = System.currentTimeMillis(); /** * Instantiates a new custom trait * * @param name

The name of the new trait

*/ protected CustomTrait(String name) { super(name); } /** * Sets the trait settings object containing this trait's configuration * * @param config

The trait settings to use

*/ protected void setTraitSettings(TraitSettings config) { this.config = config; } /** * Gets the settings used by this trait * * @return

The settings used by this trait

*/ public @Nullable Settings getTraitSettings() { return this.config; } /** * Starts a new session, and prepares to repair the player's item * * @param player

The player to start the session for

*/ public abstract void startSession(@NotNull Player player); /** * Tries to continue the session for the given player * * @param player

The player to continue the session for

*/ public void continueSession(@NotNull Player player) { //Another player is using the blacksmith if (session.isNotInSession(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 = showExactTime(); sendNPCMessage(this.npc, player, StringFormatter.replacePlaceholder(config.getBusyWorkingMessage(), "{time}", TimeFormatter.formatTime(showExactTime, timeRemaining))); return; } if (session.isSessionInvalid()) { //Quit if the player cannot afford, or has changed their item session = null; } else { //Start reforging for the player startMainAction(npc, player); } } /** * Gets whether this blacksmith is already in a reforging session * * @return

Whether already in a salvage 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(@NotNull UUID playerUUID, @NotNull Calendar waitUntil) { coolDowns.put(playerUUID, waitUntil); } /** * Unsets the session of this scrapper, making it ready for another round */ public void unsetSession() { this.session = null; } /** * 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(@NotNull 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.use")) { 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) (coolDowns.get(playerId).getTimeInMillis() - calendar.getTimeInMillis()) / 1000; boolean exactTime = showExactTime(); sendNPCMessage(this.npc, player, StringFormatter.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 Entity entity = npc.getEntity(); if (entity == null) { BlacksmithPlugin.error("NPC session could not be started, as the NPC does not have a valid entity"); return false; } if (session != null && !session.isRunning() && (System.currentTimeMillis() > currentSessionStartTime + 10 * 1000 || entity.getLocation().distance(session.getPlayer().getLocation()) > 20)) { session = null; } return true; } /** * Gets whether to show exact time when displaying the remaining time left * * @return

Whether to show exact time, or vague words

*/ protected abstract boolean showExactTime(); /** * Starts reforging the player's item * * @param npc

The NPC performing the reforge

* @param player

The player that initiated the reforge

*/ private void startMainAction(@NotNull NPC npc, @NotNull Player player) { sendNPCMessage(this.npc, player, config.getStartWorkingMessage()); boolean isBlacksmith = this instanceof BlacksmithTrait; if (isBlacksmith) { EconomyManager.withdrawBlacksmith(player); } else if (this.session instanceof SalvageSession salvageSession) { EconomyManager.withdrawScrapper(player, salvageSession.salvageMethod); } PlayerInventory playerInventory = player.getInventory(); ItemStack heldItem = player.getInventory().getItemInMainHand(); session.scheduleAction(); //Display the item in the NPC's hand Entity entity = npc.getEntity(); if (entity instanceof Player playerNPC) { playerNPC.getInventory().setItemInMainHand(heldItem); } else if (entity instanceof LivingEntity) { EntityEquipment equipment = ((LivingEntity) entity).getEquipment(); if (equipment != null) { ((LivingEntity) entity).getEquipment().setItemInMainHand(heldItem); } else { BlacksmithPlugin.warn("Failed to update NPC item, as its equipment was irretrievable."); } } //Remove the item from the player's inventory if (this.session instanceof SalvageSession salvageSession) { // For scrappers, just reduce the amounts of items, unless the remaining stack is salvaged int amount = salvageSession.getItemsConsumed(); if (amount != heldItem.getAmount()) { heldItem.setAmount(heldItem.getAmount() - amount); playerInventory.setItemInMainHand(heldItem); return; } } playerInventory.setItemInMainHand(null); } }