Adds most of the code necessary for #11

This commit is contained in:
Kristian Knarvik 2022-10-14 21:45:57 +02:00
parent 0c6a28d7df
commit 0357de1cf7
10 changed files with 404 additions and 28 deletions

View File

@ -31,7 +31,12 @@ public enum GlobalSetting {
/** /**
* Whether the cost should increase for damage taken, as opposed to increase for durability present * 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 path;
private final String parent; private final String parent;

View File

@ -180,6 +180,15 @@ public class GlobalSettings {
return asBoolean(GlobalSetting.NATURAL_COST); 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 * Gets the base price for the given material
* *

View File

@ -70,13 +70,14 @@ public enum NPCSetting {
* The message displayed when the blacksmith is already reforging something for the player * The message displayed when the blacksmith is already reforging something for the player
*/ */
BUSY_WITH_REFORGE_MESSAGE("messages.busyReforgeMessage", SettingValueType.STRING, 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 * The message displayed if the player has to wait for the cool-down to expire
*/ */
COOL_DOWN_UNEXPIRED_MESSAGE("messages.coolDownUnexpiredMessage", SettingValueType.STRING, 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 * The message displayed when displaying the cost of reforging the held item to the player

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -10,25 +10,155 @@ import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceho
*/ */
public enum TranslatableMessage { public enum TranslatableMessage {
/**
* The message displayed when a configuration value has been successfully changed
*/
VALUE_CHANGED, VALUE_CHANGED,
/**
* The message displayed when a configuration value for one material or enchantment has been successfully changed
*/
VALUE_FOR_ITEM_CHANGED, VALUE_FOR_ITEM_CHANGED,
/**
* The message displayed when showing the current value of a configuration option
*/
CURRENT_VALUE, CURRENT_VALUE,
/**
* The message displayed when showing the current value of a configuration option for one material or enchantment
*/
CURRENT_VALUE_FOR_ITEM, CURRENT_VALUE_FOR_ITEM,
/**
* The translation of enchantment
*/
ITEM_TYPE_ENCHANTMENT, ITEM_TYPE_ENCHANTMENT,
/**
* The translation of material
*/
ITEM_TYPE_MATERIAL, ITEM_TYPE_MATERIAL,
/**
* The message displayed when showing the "raw" value of messages
*/
RAW_VALUE, RAW_VALUE,
/**
* The message displayed when trying to change a blacksmith value before selecting a blacksmith
*/
NO_NPC_SELECTED, NO_NPC_SELECTED,
/**
* The message displayed if trying to change the default value of reforge-able item using commands
*/
DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE, DEFAULT_REFORGE_ABLE_ITEMS_UNCHANGEABLE,
/**
* The message displayed if a string list is required, but something else is given
*/
INPUT_STRING_LIST_REQUIRED, INPUT_STRING_LIST_REQUIRED,
/**
* The message displayed if a value between 0 and 100 is required, but something else is given
*/
INPUT_PERCENTAGE_REQUIRED, INPUT_PERCENTAGE_REQUIRED,
/**
* The message displayed if a string is required, but something else is given
*/
INPUT_STRING_REQUIRED, INPUT_STRING_REQUIRED,
/**
* The message displayed if a positive double is required, but something else is given
*/
INPUT_POSITIVE_DOUBLE_REQUIRED, INPUT_POSITIVE_DOUBLE_REQUIRED,
/**
* The message displayed if a positive integer is required, but something else is given
*/
INPUT_POSITIVE_INTEGER_REQUIRED, INPUT_POSITIVE_INTEGER_REQUIRED,
/**
* The message displayed if a player is missing the required permission for an action
*/
PERMISSION_DENIED, PERMISSION_DENIED,
/**
* The message displayed if this plugin is successfully reloaded
*/
PLUGIN_RELOADED, PLUGIN_RELOADED,
/**
* The message displayed if a filter is specified which isn't supported by the specified preset
*/
INVALID_FILTER_FOR_PRESET, INVALID_FILTER_FOR_PRESET,
/**
* The message displayed if an invalid preset or an invalid filter is specified
*/
INVALID_PRESET_OR_FILTER, 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 * Gets the message to display when displaying the raw value of messages

View File

@ -5,7 +5,7 @@ import net.citizensnpcs.api.trait.Trait;
import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.DataKey;
import net.knarcraft.blacksmith.BlacksmithPlugin; import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.NPCSettings; 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.manager.EconomyManager;
import net.knarcraft.blacksmith.util.ItemHelper; import net.knarcraft.blacksmith.util.ItemHelper;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import static net.knarcraft.blacksmith.formatting.StringFormatter.replacePlaceholder;
import static net.knarcraft.blacksmith.formatting.StringFormatter.sendNPCMessage; 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> * @return <p>True if preparations were successful. False if a session shouldn't be started</p>
*/ */
public boolean prepareForSession(Player player) { 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 cool-down has been disabled after it was set for this player, remove the cool-down
if (config.getDisableCoolDown() && coolDowns.get(player.getUniqueId()) != null) { if (config.getDisableCoolDown() && coolDowns.get(playerId) != null) {
coolDowns.remove(player.getUniqueId()); coolDowns.remove(playerId);
} }
//Deny if permission is missing //Deny if permission is missing
if (!player.hasPermission("blacksmith.reforge")) { 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 //Deny if on cool-down, or remove cool-down if expired
if (coolDowns.get(player.getUniqueId()) != null) { if (coolDowns.get(playerId) != null) {
if (!Calendar.getInstance().after(coolDowns.get(player.getUniqueId()))) { Calendar calendar = Calendar.getInstance();
sendNPCMessage(this.npc, player, config.getCoolDownUnexpiredMessage()); 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; 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 (session != null) {
if (System.currentTimeMillis() > _sessionStart + 10 * 1000 || if (System.currentTimeMillis() > _sessionStart + 10 * 1000 &&
this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20) { this.npc.getEntity().getLocation().distance(session.getPlayer().getLocation()) > 20) {
session = null; session = null;
} }
@ -149,7 +155,10 @@ public class BlacksmithTrait extends Trait {
//The blacksmith is already reforging for the player //The blacksmith is already reforging for the player
if (session.isRunning()) { 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; return;
} }
if (session.endSession()) { 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 //Refuse if not repairable, or if reforge-able items is set, but doesn't include the held item
List<Material> reforgeAbleItems = config.getReforgeAbleItems(); List<Material> reforgeAbleItems = config.getReforgeAbleItems();
if (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) { if (!isRepairable(hand) || (!reforgeAbleItems.isEmpty() && !reforgeAbleItems.contains(hand.getType()))) {
String invalidMessage = StringFormatter.replacePlaceholder(config.getInvalidItemMessage(), String invalidMessage = replacePlaceholder(config.getInvalidItemMessage(),
"{title}", config.getBlacksmithTitle()); "{title}", config.getBlacksmithTitle());
sendNPCMessage(this.npc, player, invalidMessage); sendNPCMessage(this.npc, player, invalidMessage);
return; return;
@ -189,7 +198,8 @@ public class BlacksmithTrait extends Trait {
//Tell the player the cost of repairing the item //Tell the player the cost of repairing the item
String cost = EconomyManager.formatCost(player); String cost = EconomyManager.formatCost(player);
String itemName = hand.getType().name().toLowerCase().replace('_', ' '); 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));
} }
/** /**

View File

@ -12,6 +12,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.scheduler.BukkitScheduler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
@ -32,6 +33,7 @@ public class ReforgeSession implements Runnable {
private final NPC npc; private final NPC npc;
private final ItemStack itemToReforge; private final ItemStack itemToReforge;
private int taskId; private int taskId;
private long finishTime = 0;
private final NPCSettings config; private final NPCSettings config;
private static final String[] enchantments = new String[Enchantment.values().length]; private static final String[] enchantments = new String[Enchantment.values().length];
private static final Random random = new Random(); private static final Random random = new Random();
@ -48,14 +50,26 @@ public class ReforgeSession implements Runnable {
this.blacksmithTrait = blacksmithTrait; this.blacksmithTrait = blacksmithTrait;
this.player = player; this.player = player;
this.npc = npc; this.npc = npc;
itemToReforge = player.getInventory().getItemInMainHand(); this.itemToReforge = player.getInventory().getItemInMainHand();
this.config = config; this.config = config;
//Populate enchantments the first time this is run
if (enchantments[0] == null) {
int i = 0; int i = 0;
for (Enchantment enchantment : Enchantment.values()) { for (Enchantment enchantment : Enchantment.values()) {
enchantments[i++] = enchantment.getKey().toString(); 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 * 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 * Begins the actual item reforging
*/ */
public void beginReforge() { public void beginReforge() {
BukkitScheduler scheduler = BlacksmithPlugin.getInstance().getServer().getScheduler();
int reforgeDelay;
if (!config.getDisableCoolDown()) { if (!config.getDisableCoolDown()) {
//Finish the reforging after a random delay between the max and min //Finish the reforging after a random delay between the max and min
taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask( reforgeDelay = new Random().nextInt(config.getMaxReforgeDelay()) + config.getMinReforgeDelay();
BlacksmithPlugin.getInstance(), this, (new Random().nextInt(config.getMaxReforgeDelay()) +
config.getMinReforgeDelay()) * 20L);
} else { } else {
//Finish the reforging as soon as possible //Finish the reforging as soon as possible
taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask( reforgeDelay = 0;
BlacksmithPlugin.getInstance(), this, 0);
} }
this.finishTime = System.currentTimeMillis() + (reforgeDelay * 1000L);
taskId = scheduler.scheduleSyncDelayedTask(BlacksmithPlugin.getInstance(), this, reforgeDelay * 20L);
} }
/** /**

View File

@ -21,6 +21,10 @@ global:
# blacksmith behavior instead # blacksmith behavior instead
useNaturalCost: true 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 # 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 # Citizens NPC file, or use the /blacksmith command
defaults: defaults:
@ -61,10 +65,10 @@ defaults:
busyPlayerMessage: "&cI'm busy at the moment. Come back later!" busyPlayerMessage: "&cI'm busy at the moment. Come back later!"
# The message to display when the blacksmith is working on the reforging # 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 # 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 # 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!" costMessage: "&eIt will cost &a{cost}&e to reforge that &a{item}&e! Click again to reforge!"

View File

@ -18,3 +18,14 @@ en:
INVALID_FILTER_FOR_PRESET: "The specified filter is not valid for that preset" 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" 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"