Adds most of the code necessary for #11
This commit is contained in:
		@@ -31,7 +31,12 @@ public enum GlobalSetting {
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether the cost should increase for damage taken, as opposed to increase for durability present
 | 
			
		||||
     */
 | 
			
		||||
    NATURAL_COST("global.useNaturalCost", SettingValueType.BOOLEAN, true, "useNaturalCost");
 | 
			
		||||
    NATURAL_COST("global.useNaturalCost", SettingValueType.BOOLEAN, true, "useNaturalCost"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Whether to show exact time when displaying the wait time for a reforging or the cool-down
 | 
			
		||||
     */
 | 
			
		||||
    SHOW_EXACT_TIME("global.showExactTime", SettingValueType.BOOLEAN, false, "showExactTime");
 | 
			
		||||
 | 
			
		||||
    private final String path;
 | 
			
		||||
    private final String parent;
 | 
			
		||||
 
 | 
			
		||||
@@ -180,6 +180,15 @@ public class GlobalSettings {
 | 
			
		||||
        return asBoolean(GlobalSetting.NATURAL_COST);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets whether to show exact time for reforging wait-time, and for wait-time between sessions
 | 
			
		||||
     *
 | 
			
		||||
     * @return <p>Whether to show exact time</p>
 | 
			
		||||
     */
 | 
			
		||||
    public boolean getShowExactTime() {
 | 
			
		||||
        return asBoolean(GlobalSetting.SHOW_EXACT_TIME);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the base price for the given material
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -70,13 +70,14 @@ public enum NPCSetting {
 | 
			
		||||
     * The message displayed when the blacksmith is already reforging something for the player
 | 
			
		||||
     */
 | 
			
		||||
    BUSY_WITH_REFORGE_MESSAGE("messages.busyReforgeMessage", SettingValueType.STRING,
 | 
			
		||||
            "&cI'm working on it. Be patient!", "busyReforgeMessage"),
 | 
			
		||||
            "&cI'm working on it. Be patient! I'll finish {time}!", "busyReforgeMessage"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if the player has to wait for the cool-down to expire
 | 
			
		||||
     */
 | 
			
		||||
    COOL_DOWN_UNEXPIRED_MESSAGE("messages.coolDownUnexpiredMessage", SettingValueType.STRING,
 | 
			
		||||
            "&cYou've already had your chance! Give me a break!", "coolDownUnexpiredMessage"),
 | 
			
		||||
            "&cYou've already had your chance! Give me a break! I'll be ready {time}!",
 | 
			
		||||
            "coolDownUnexpiredMessage"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when displaying the cost of reforging the held item to the player
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,126 @@
 | 
			
		||||
package net.knarcraft.blacksmith.formatting;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A utility for formatting a string specifying an amount of time
 | 
			
		||||
 */
 | 
			
		||||
public final class TimeFormatter {
 | 
			
		||||
 | 
			
		||||
    private static Map<Double, TranslatableMessage[]> timeUnits;
 | 
			
		||||
    private static List<Double> sortedUnits;
 | 
			
		||||
 | 
			
		||||
    private TimeFormatter() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Formats the given number of remaining seconds to the appropriate string
 | 
			
		||||
     *
 | 
			
		||||
     * @param exact   <p>Whether to display the exact number of seconds, or some unclear expression</p>
 | 
			
		||||
     * @param seconds <p>The number of seconds remaining</p>
 | 
			
		||||
     * @return <p>The time formatted correctly</p>
 | 
			
		||||
     */
 | 
			
		||||
    public static String formatTime(boolean exact, int seconds) {
 | 
			
		||||
        if (exact) {
 | 
			
		||||
            return getDurationString(seconds);
 | 
			
		||||
        } else {
 | 
			
		||||
            return formatUnclearTime(seconds);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets a time format using vague wording to specify a known interval
 | 
			
		||||
     *
 | 
			
		||||
     * @param seconds <p>The number of seconds to format</p>
 | 
			
		||||
     * @return <p>Text describing approximate time remaining</p>
 | 
			
		||||
     */
 | 
			
		||||
    private static String formatUnclearTime(int seconds) {
 | 
			
		||||
        List<TimeInterval> intervals = Arrays.stream(TimeInterval.values()).sorted().toList();
 | 
			
		||||
        for (TimeInterval interval : intervals) {
 | 
			
		||||
            if (seconds < interval.getIntervalMax()) {
 | 
			
		||||
                //Use the set message, or use the default
 | 
			
		||||
                //TODO: Check for commas in the message. If present, split on the comma and choose a random expression
 | 
			
		||||
                String text = Translator.getTranslatedMessage(TranslatableMessage.valueOf(interval.name()));
 | 
			
		||||
                if (text != null && !text.trim().isEmpty()) {
 | 
			
		||||
                    return text;
 | 
			
		||||
                } else {
 | 
			
		||||
                    return interval.getDefaultText();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return TimeInterval.INTERVAL_MORE_THAN_5_MINUTES.getDefaultText();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the string used for displaying this sign's duration
 | 
			
		||||
     *
 | 
			
		||||
     * @return <p>The string used for displaying this sign's duration</p>
 | 
			
		||||
     */
 | 
			
		||||
    public static String getDurationString(int duration) {
 | 
			
		||||
        if (duration == 0) {
 | 
			
		||||
            return Translator.getTranslatedMessage(TranslatableMessage.UNIT_NOW);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (sortedUnits == null) {
 | 
			
		||||
                initializeUnits();
 | 
			
		||||
            }
 | 
			
		||||
            for (Double unit : sortedUnits) {
 | 
			
		||||
                if (duration / unit >= 1) {
 | 
			
		||||
                    double units = round(duration / unit);
 | 
			
		||||
                    return formatDurationString(units, timeUnits.get(unit)[units == 1 ? 0 : 1],
 | 
			
		||||
                            (units * 10) % 10 == 0);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return formatDurationString(duration, TranslatableMessage.UNIT_SECONDS, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Rounds a number to its last two digits
 | 
			
		||||
     *
 | 
			
		||||
     * @param number <p>The number to round</p>
 | 
			
		||||
     * @return <p>The rounded number</p>
 | 
			
		||||
     */
 | 
			
		||||
    private static double round(double number) {
 | 
			
		||||
        return Math.round(number * 100.0) / 100.0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Formats a duration string
 | 
			
		||||
     *
 | 
			
		||||
     * @param duration            <p>The duration to display</p>
 | 
			
		||||
     * @param translatableMessage <p>The time unit to display</p>
 | 
			
		||||
     * @param castToInt           <p>Whether to cast the duration to an int</p>
 | 
			
		||||
     * @return <p>The formatted duration string</p>
 | 
			
		||||
     */
 | 
			
		||||
    private static String formatDurationString(double duration, TranslatableMessage translatableMessage, boolean castToInt) {
 | 
			
		||||
        String durationFormat = Translator.getTranslatedMessage(TranslatableMessage.DURATION_FORMAT);
 | 
			
		||||
        durationFormat = replacePlaceholder(durationFormat, "{unit}",
 | 
			
		||||
                Translator.getTranslatedMessage(translatableMessage));
 | 
			
		||||
        return replacePlaceholder(durationFormat, "{duration}", castToInt ? String.valueOf((int) duration) :
 | 
			
		||||
                String.valueOf(duration));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes the mapping of available time units for formatting permission sign duration
 | 
			
		||||
     */
 | 
			
		||||
    private static void initializeUnits() {
 | 
			
		||||
        double minute = 60;
 | 
			
		||||
 | 
			
		||||
        timeUnits = new HashMap<>();
 | 
			
		||||
        timeUnits.put(minute, new TranslatableMessage[]{TranslatableMessage.UNIT_MINUTE, TranslatableMessage.UNIT_MINUTES});
 | 
			
		||||
        timeUnits.put(1D, new TranslatableMessage[]{TranslatableMessage.UNIT_SECOND, TranslatableMessage.UNIT_SECONDS});
 | 
			
		||||
 | 
			
		||||
        sortedUnits = new ArrayList<>(timeUnits.keySet());
 | 
			
		||||
        Collections.sort(sortedUnits);
 | 
			
		||||
        Collections.reverse(sortedUnits);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
package net.knarcraft.blacksmith.formatting;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The important intervals when not using exact wait times
 | 
			
		||||
 */
 | 
			
		||||
public enum TimeInterval {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Less than 10 seconds left
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_10_SECONDS("momentarily", 10),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Less than 30 seconds left
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_30_SECONDS("in a little while", 30),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Less than 1 minute left
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_1_MINUTE("in a while", 60),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Less than 5 minutes left
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_5_MINUTES("after some time", 300),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * More than 5 minutes left
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_MORE_THAN_5_MINUTES("in quite a while", Integer.MAX_VALUE);
 | 
			
		||||
 | 
			
		||||
    private final String defaultText;
 | 
			
		||||
    private final int maxSeconds;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Instantiates a new time interval
 | 
			
		||||
     *
 | 
			
		||||
     * @param defaultText <p>The default text used to describe the time interval</p>
 | 
			
		||||
     * @param maxSeconds  <p>The maximum number of seconds to fall within this interval</p>
 | 
			
		||||
     */
 | 
			
		||||
    TimeInterval(String defaultText, int maxSeconds) {
 | 
			
		||||
        this.defaultText = defaultText;
 | 
			
		||||
        this.maxSeconds = maxSeconds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the default text to display for this interval
 | 
			
		||||
     *
 | 
			
		||||
     * @return <p>The default text to display for this interval</p>
 | 
			
		||||
     */
 | 
			
		||||
    public String getDefaultText() {
 | 
			
		||||
        return this.defaultText;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the maximum number of seconds before exceeding this interval
 | 
			
		||||
     *
 | 
			
		||||
     * @return <p>The max seconds of this interval</p>
 | 
			
		||||
     */
 | 
			
		||||
    public int getIntervalMax() {
 | 
			
		||||
        return maxSeconds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -10,25 +10,155 @@ import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceho
 | 
			
		||||
 */
 | 
			
		||||
public enum TranslatableMessage {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when a configuration value has been successfully changed
 | 
			
		||||
     */
 | 
			
		||||
    VALUE_CHANGED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when a configuration value for one material or enchantment has been successfully changed
 | 
			
		||||
     */
 | 
			
		||||
    VALUE_FOR_ITEM_CHANGED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when showing the current value of a configuration option
 | 
			
		||||
     */
 | 
			
		||||
    CURRENT_VALUE,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when showing the current value of a configuration option for one material or enchantment
 | 
			
		||||
     */
 | 
			
		||||
    CURRENT_VALUE_FOR_ITEM,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The translation of enchantment
 | 
			
		||||
     */
 | 
			
		||||
    ITEM_TYPE_ENCHANTMENT,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The translation of material
 | 
			
		||||
     */
 | 
			
		||||
    ITEM_TYPE_MATERIAL,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when showing the "raw" value of messages
 | 
			
		||||
     */
 | 
			
		||||
    RAW_VALUE,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when trying to change a blacksmith value before selecting a blacksmith
 | 
			
		||||
     */
 | 
			
		||||
    NO_NPC_SELECTED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if trying to change the default value of reforge-able item using commands
 | 
			
		||||
     */
 | 
			
		||||
    DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if a string list is required, but something else is given
 | 
			
		||||
     */
 | 
			
		||||
    INPUT_STRING_LIST_REQUIRED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if a value between 0 and 100 is required, but something else is given
 | 
			
		||||
     */
 | 
			
		||||
    INPUT_PERCENTAGE_REQUIRED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if a string is required, but something else is given
 | 
			
		||||
     */
 | 
			
		||||
    INPUT_STRING_REQUIRED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if a positive double is required, but something else is given
 | 
			
		||||
     */
 | 
			
		||||
    INPUT_POSITIVE_DOUBLE_REQUIRED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if a positive integer is required, but something else is given
 | 
			
		||||
     */
 | 
			
		||||
    INPUT_POSITIVE_INTEGER_REQUIRED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if a player is missing the required permission for an action
 | 
			
		||||
     */
 | 
			
		||||
    PERMISSION_DENIED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if this plugin is successfully reloaded
 | 
			
		||||
     */
 | 
			
		||||
    PLUGIN_RELOADED,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if a filter is specified which isn't supported by the specified preset
 | 
			
		||||
     */
 | 
			
		||||
    INVALID_FILTER_FOR_PRESET,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed if an invalid preset or an invalid filter is specified
 | 
			
		||||
     */
 | 
			
		||||
    INVALID_PRESET_OR_FILTER,
 | 
			
		||||
    PRESET_MATERIALS;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The message displayed when showing which materials are included in a preset
 | 
			
		||||
     */
 | 
			
		||||
    PRESET_MATERIALS,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The format for displaying the exact duration of a blacksmith's cool-down or delay
 | 
			
		||||
     */
 | 
			
		||||
    DURATION_FORMAT,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display for 0 seconds
 | 
			
		||||
     */
 | 
			
		||||
    UNIT_NOW,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display for 1 second
 | 
			
		||||
     */
 | 
			
		||||
    UNIT_SECOND,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display for a number of seconds
 | 
			
		||||
     */
 | 
			
		||||
    UNIT_SECONDS,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display for 1 minute
 | 
			
		||||
     */
 | 
			
		||||
    UNIT_MINUTE,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display for a number of minutes
 | 
			
		||||
     */
 | 
			
		||||
    UNIT_MINUTES,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display when describing less than 10 seconds remaining
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_10_SECONDS,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display when describing less than 30 seconds remaining
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_30_SECONDS,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display when describing less than 1 minute remaining
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_1_MINUTE,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display when describing less than 5 minutes remaining
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_LESS_THAN_5_MINUTES,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The text to display when describing more than 5 minutes remaining
 | 
			
		||||
     */
 | 
			
		||||
    INTERVAL_MORE_THAN_5_MINUTES;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the message to display when displaying the raw value of messages
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ 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.StringFormatter;
 | 
			
		||||
import net.knarcraft.blacksmith.formatting.TimeFormatter;
 | 
			
		||||
import net.knarcraft.blacksmith.manager.EconomyManager;
 | 
			
		||||
import net.knarcraft.blacksmith.util.ItemHelper;
 | 
			
		||||
import org.bukkit.Bukkit;
 | 
			
		||||
@@ -23,6 +23,7 @@ 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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -107,9 +108,10 @@ public class BlacksmithTrait extends Trait {
 | 
			
		||||
     * @return <p>True if preparations were successful. False if a session shouldn't be started</p>
 | 
			
		||||
     */
 | 
			
		||||
    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(player.getUniqueId()) != null) {
 | 
			
		||||
            coolDowns.remove(player.getUniqueId());
 | 
			
		||||
        if (config.getDisableCoolDown() && coolDowns.get(playerId) != null) {
 | 
			
		||||
            coolDowns.remove(playerId);
 | 
			
		||||
        }
 | 
			
		||||
        //Deny if permission is missing
 | 
			
		||||
        if (!player.hasPermission("blacksmith.reforge")) {
 | 
			
		||||
@@ -117,17 +119,21 @@ public class BlacksmithTrait extends Trait {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Deny if on cool-down, or remove cool-down if expired
 | 
			
		||||
        if (coolDowns.get(player.getUniqueId()) != null) {
 | 
			
		||||
            if (!Calendar.getInstance().after(coolDowns.get(player.getUniqueId()))) {
 | 
			
		||||
                sendNPCMessage(this.npc, player, config.getCoolDownUnexpiredMessage());
 | 
			
		||||
        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(player.getUniqueId());
 | 
			
		||||
            coolDowns.remove(playerId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //If already in a session, but the player has failed to interact, or left the blacksmith, allow a new session
 | 
			
		||||
        //If already in a session, but the player has failed to interact, and left the blacksmith, allow a new session
 | 
			
		||||
        if (session != null) {
 | 
			
		||||
            if (System.currentTimeMillis() > _sessionStart + 10 * 1000 ||
 | 
			
		||||
            if (System.currentTimeMillis() > _sessionStart + 10 * 1000 &&
 | 
			
		||||
                    this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20) {
 | 
			
		||||
                session = null;
 | 
			
		||||
            }
 | 
			
		||||
@@ -149,7 +155,10 @@ public class BlacksmithTrait extends Trait {
 | 
			
		||||
 | 
			
		||||
        //The blacksmith is already reforging for the player
 | 
			
		||||
        if (session.isRunning()) {
 | 
			
		||||
            sendNPCMessage(this.npc, player, config.getBusyReforgingMessage());
 | 
			
		||||
            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()) {
 | 
			
		||||
@@ -171,7 +180,7 @@ public class BlacksmithTrait extends Trait {
 | 
			
		||||
        //Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item
 | 
			
		||||
        List<Material> reforgeAbleItems = config.getReforgeAbleItems();
 | 
			
		||||
        if (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) {
 | 
			
		||||
            String invalidMessage = StringFormatter.replacePlaceholder(config.getInvalidItemMessage(),
 | 
			
		||||
            String invalidMessage = replacePlaceholder(config.getInvalidItemMessage(),
 | 
			
		||||
                    "{title}", config.getBlacksmithTitle());
 | 
			
		||||
            sendNPCMessage(this.npc, player, invalidMessage);
 | 
			
		||||
            return;
 | 
			
		||||
@@ -189,7 +198,8 @@ public class BlacksmithTrait extends Trait {
 | 
			
		||||
        //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));
 | 
			
		||||
        sendNPCMessage(this.npc, player, config.getCostMessage().replace("{cost}", cost).replace("{item}",
 | 
			
		||||
                itemName));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import org.bukkit.entity.Player;
 | 
			
		||||
import org.bukkit.inventory.ItemStack;
 | 
			
		||||
import org.bukkit.inventory.meta.Damageable;
 | 
			
		||||
import org.bukkit.inventory.meta.ItemMeta;
 | 
			
		||||
import org.bukkit.scheduler.BukkitScheduler;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Calendar;
 | 
			
		||||
@@ -32,6 +33,7 @@ public class ReforgeSession implements Runnable {
 | 
			
		||||
    private final NPC npc;
 | 
			
		||||
    private final ItemStack itemToReforge;
 | 
			
		||||
    private int taskId;
 | 
			
		||||
    private long finishTime = 0;
 | 
			
		||||
    private final NPCSettings config;
 | 
			
		||||
    private static final String[] enchantments = new String[Enchantment.values().length];
 | 
			
		||||
    private static final Random random = new Random();
 | 
			
		||||
@@ -48,15 +50,27 @@ public class ReforgeSession implements Runnable {
 | 
			
		||||
        this.blacksmithTrait = blacksmithTrait;
 | 
			
		||||
        this.player = player;
 | 
			
		||||
        this.npc = npc;
 | 
			
		||||
        itemToReforge = player.getInventory().getItemInMainHand();
 | 
			
		||||
        this.itemToReforge = player.getInventory().getItemInMainHand();
 | 
			
		||||
        this.config = config;
 | 
			
		||||
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        for (Enchantment enchantment : Enchantment.values()) {
 | 
			
		||||
            enchantments[i++] = enchantment.getKey().toString();
 | 
			
		||||
        //Populate enchantments the first time this is run
 | 
			
		||||
        if (enchantments[0] == null) {
 | 
			
		||||
            int i = 0;
 | 
			
		||||
            for (Enchantment enchantment : Enchantment.values()) {
 | 
			
		||||
                enchantments[i++] = enchantment.getKey().toString();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the time in milliseconds when this reforging session will finish
 | 
			
		||||
     *
 | 
			
		||||
     * @return <p>The time the reforging will finish</p>
 | 
			
		||||
     */
 | 
			
		||||
    public long getFinishTime() {
 | 
			
		||||
        return this.finishTime;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Runs the actual reforge which fixes the item and gives it back to the player
 | 
			
		||||
     */
 | 
			
		||||
@@ -237,16 +251,17 @@ public class ReforgeSession implements Runnable {
 | 
			
		||||
     * Begins the actual item reforging
 | 
			
		||||
     */
 | 
			
		||||
    public void beginReforge() {
 | 
			
		||||
        BukkitScheduler scheduler = BlacksmithPlugin.getInstance().getServer().getScheduler();
 | 
			
		||||
        int reforgeDelay;
 | 
			
		||||
        if (!config.getDisableCoolDown()) {
 | 
			
		||||
            //Finish the reforging after a random delay between the max and min
 | 
			
		||||
            taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(
 | 
			
		||||
                    BlacksmithPlugin.getInstance(), this, (new Random().nextInt(config.getMaxReforgeDelay()) +
 | 
			
		||||
                            config.getMinReforgeDelay()) * 20L);
 | 
			
		||||
            reforgeDelay = new Random().nextInt(config.getMaxReforgeDelay()) + config.getMinReforgeDelay();
 | 
			
		||||
        } else {
 | 
			
		||||
            //Finish the reforging as soon as possible
 | 
			
		||||
            taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(
 | 
			
		||||
                    BlacksmithPlugin.getInstance(), this, 0);
 | 
			
		||||
            reforgeDelay = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this.finishTime = System.currentTimeMillis() + (reforgeDelay * 1000L);
 | 
			
		||||
        taskId = scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), this, reforgeDelay * 20L);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,10 @@ global:
 | 
			
		||||
  #  blacksmith behavior instead
 | 
			
		||||
  useNaturalCost: true
 | 
			
		||||
 | 
			
		||||
  # Exact time displays the exact number of seconds and minutes remaining as part of the reforging cool-down and
 | 
			
		||||
  #  reforging delay messages, instead of just vaguely hinting at the remaining time.
 | 
			
		||||
  showExactTime: false
 | 
			
		||||
 | 
			
		||||
# The settings which are set to any new NPC. To change any of these settings for an existing NPC, you must change the 
 | 
			
		||||
#  Citizens NPC file, or use the /blacksmith command
 | 
			
		||||
defaults:
 | 
			
		||||
@@ -61,10 +65,10 @@ defaults:
 | 
			
		||||
    busyPlayerMessage: "&cI'm busy at the moment. Come back later!"
 | 
			
		||||
 | 
			
		||||
    # The message to display when the blacksmith is working on the reforging
 | 
			
		||||
    busyReforgeMessage: "&cI'm working on it. Be patient!"
 | 
			
		||||
    busyReforgeMessage: "&cI'm working on it. Be patient! I'll finish {time}!"
 | 
			
		||||
 | 
			
		||||
    # The message to display when the blacksmith is still on a cool-down from the previous re-forging
 | 
			
		||||
    coolDownUnexpiredMessage: "&cYou've already had your chance! Give me a break!"
 | 
			
		||||
    coolDownUnexpiredMessage: "&cYou've already had your chance! Give me a break! I'll be ready {time}!"
 | 
			
		||||
 | 
			
		||||
    # The message to display when informing a player about the reforging cost
 | 
			
		||||
    costMessage: "&eIt will cost &a{cost}&e to reforge that &a{item}&e! Click again to reforge!"
 | 
			
		||||
 
 | 
			
		||||
@@ -17,4 +17,15 @@ en:
 | 
			
		||||
  PLUGIN_RELOADED: "Blacksmith config reloaded!"
 | 
			
		||||
  INVALID_FILTER_FOR_PRESET: "The specified filter is not valid for that preset"
 | 
			
		||||
  INVALID_PRESET_OR_FILTER: "You specified an invalid preset or an invalid filter"
 | 
			
		||||
  PRESET_MATERIALS: "Materials in preset: {materials}"
 | 
			
		||||
  PRESET_MATERIALS: "Materials in preset: {materials}"
 | 
			
		||||
  DURATION_FORMAT: "{time} {unit}"
 | 
			
		||||
  UNIT_NOW: "imminently"
 | 
			
		||||
  UNIT_SECOND: "second"
 | 
			
		||||
  UNIT_SECONDS: "seconds"
 | 
			
		||||
  UNIT_MINUTE: "minute"
 | 
			
		||||
  UNIT_MINUTES: "minutes"
 | 
			
		||||
  INTERVAL_LESS_THAN_10_SECONDS: "momentarily"
 | 
			
		||||
  INTERVAL_LESS_THAN_30_SECONDS: "in a little while"
 | 
			
		||||
  INTERVAL_LESS_THAN_1_MINUTE: "in a while"
 | 
			
		||||
  INTERVAL_LESS_THAN_5_MINUTES: "after some time"
 | 
			
		||||
  INTERVAL_MORE_THAN_5_MINUTES: "in quite a while"
 | 
			
		||||
		Reference in New Issue
	
	Block a user