Support meta for Books, Potions and more, fixes #107. Bump version

This commit is contained in:
HappyPikachu 2018-10-15 00:04:27 -04:00
parent 502ea53f9a
commit c2393107e1
9 changed files with 181 additions and 37 deletions

View File

@ -3,7 +3,7 @@
<groupId>me.blackvein.quests</groupId>
<artifactId>quests</artifactId>
<version>3.3.7</version>
<version>3.3.8</version>
<name>quests</name>
<url>https://github.com/FlyingPikachu/Quests/</url>
<packaging>jar</packaging>

View File

@ -783,7 +783,7 @@ public class EventFactory implements ConversationAbandonedListener {
LinkedList<ItemStack> items = (LinkedList<ItemStack>) context.getSessionData(CK.E_ITEMS);
LinkedList<String> lines = new LinkedList<String>();
for (ItemStack is : items) {
lines.add(ItemUtil.serialize(is));
lines.add(ItemUtil.serializeItemStack(is));
}
section.set("items", lines);
}
@ -825,15 +825,15 @@ public class EventFactory implements ConversationAbandonedListener {
ss.set("spawn-location", Quests.getLocationInfo(questMob.getSpawnLocation()));
ss.set("mob-type", questMob.getType().name());
ss.set("spawn-amounts", questMob.getSpawnAmounts());
ss.set("held-item", ItemUtil.serialize(questMob.inventory[0]));
ss.set("held-item", ItemUtil.serializeItemStack(questMob.inventory[0]));
ss.set("held-item-drop-chance", questMob.dropChances[0]);
ss.set("boots", ItemUtil.serialize(questMob.inventory[1]));
ss.set("boots", ItemUtil.serializeItemStack(questMob.inventory[1]));
ss.set("boots-drop-chance", questMob.dropChances[1]);
ss.set("leggings", ItemUtil.serialize(questMob.inventory[2]));
ss.set("leggings", ItemUtil.serializeItemStack(questMob.inventory[2]));
ss.set("leggings-drop-chance", questMob.dropChances[2]);
ss.set("chest-plate", ItemUtil.serialize(questMob.inventory[3]));
ss.set("chest-plate", ItemUtil.serializeItemStack(questMob.inventory[3]));
ss.set("chest-plate-drop-chance", questMob.dropChances[3]);
ss.set("helmet", ItemUtil.serialize(questMob.inventory[4]));
ss.set("helmet", ItemUtil.serializeItemStack(questMob.inventory[4]));
ss.set("helmet-drop-chance", questMob.dropChances[4]);
count++;
}

View File

@ -15,6 +15,7 @@ package me.blackvein.quests;
import java.text.MessageFormat;
import java.util.LinkedList;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.conversations.Conversation;
@ -34,6 +35,7 @@ import net.citizensnpcs.api.event.NPCDeathEvent;
import net.citizensnpcs.api.event.NPCLeftClickEvent;
import net.citizensnpcs.api.event.NPCRightClickEvent;
import net.citizensnpcs.api.npc.NPC;
import net.milkbowl.vault.item.Items;
public class NpcListener implements Listener {
@ -81,7 +83,15 @@ public class NpcListener implements Listener {
if (hand.hasItemMeta()) {
text += ChatColor.LIGHT_PURPLE + "" + ChatColor.ITALIC + (hand.getItemMeta().hasDisplayName() ? hand.getItemMeta().getDisplayName() + ChatColor.GRAY + " (" : "");
}
text += ChatColor.AQUA + ItemUtil.getName(hand) + (hand.getDurability() != 0 ? (":" + ChatColor.BLUE + hand.getDurability()) : "") + ChatColor.GRAY;
String base = "error";
try {
base = ChatColor.AQUA + Items.itemByType(hand.getType()).getName();
} catch (Exception ne) {
text = ChatColor.AQUA + Quester.prettyItemString(hand.getType().name());
Bukkit.getLogger().severe("Likely caused by an incompatible version of Vault. Consider updating!");
ne.printStackTrace();
}
text += ChatColor.AQUA + base + (hand.getDurability() != 0 ? (":" + ChatColor.BLUE + hand.getDurability()) : "") + ChatColor.GRAY;
if (hand.hasItemMeta()) {
text += (hand.getItemMeta().hasDisplayName() ? ")" : "");
}

View File

@ -831,7 +831,7 @@ public class QuestFactory implements ConversationAbandonedListener {
if (cc.getSessionData(CK.REW_ITEMS) != null) {
itemRews = new LinkedList<String>();
for (ItemStack is : (LinkedList<ItemStack>) cc.getSessionData(CK.REW_ITEMS)) {
itemRews.add(ItemUtil.serialize(is));
itemRews.add(ItemUtil.serializeItemStack(is));
}
}
if (cc.getSessionData(CK.REW_EXP) != null) {
@ -877,13 +877,13 @@ public class QuestFactory implements ConversationAbandonedListener {
cs.set("finish-message", finish);
cs.set("event", initialEvent);
cs.set("region", region);
cs.set("gui-display", ItemUtil.serialize(guiDisplay));
cs.set("gui-display", ItemUtil.serializeItemStack(guiDisplay));
if (moneyReq != null || questPointsReq != null || itemReqs != null && itemReqs.isEmpty() == false || permReqs != null && permReqs.isEmpty() == false || (questReqs != null && questReqs.isEmpty() == false) || (questBlocks != null && questBlocks.isEmpty() == false) || (mcMMOSkillReqs != null && mcMMOSkillReqs.isEmpty() == false) || heroesPrimaryReq != null || heroesSecondaryReq != null || customReqs != null) {
ConfigurationSection reqs = cs.createSection("requirements");
List<String> items = new LinkedList<String>();
if (itemReqs != null) {
for (ItemStack is : itemReqs) {
items.add(ItemUtil.serialize(is));
items.add(ItemUtil.serializeItemStack(is));
}
}
reqs.set("items", (items.isEmpty() == false) ? items : null);
@ -1177,7 +1177,7 @@ public class QuestFactory implements ConversationAbandonedListener {
if (deliveryItems != null && deliveryItems.isEmpty() == false) {
LinkedList<String> items = new LinkedList<String>();
for (ItemStack is : deliveryItems) {
items.add(ItemUtil.serialize(is));
items.add(ItemUtil.serializeItemStack(is));
}
stage.set("items-to-deliver", items);
} else {

View File

@ -2016,20 +2016,20 @@ public class CreateStagePrompt extends FixedSetPrompt {
if (context.getSessionData(pref + CK.S_DELIVERY_ITEMS) == null) {
text += ChatColor.GRAY + " (" + Lang.get("noneSet") + ")\n";
text += ChatColor.BLUE + "" + ChatColor.BOLD + "1" + ChatColor.RESET + ChatColor.YELLOW + " - " + Lang.get("stageEditorDeliveryAddItem") + "\n";
text += ChatColor.GRAY + "2 - " + Lang.get("stageEditorDeliveryNPCs") + " (" + Lang.get("stageEditorNoItemsSet") + ")\n";
text += ChatColor.GRAY + "" + ChatColor.BOLD + "2" + ChatColor.RESET + ChatColor.GRAY + " - " + Lang.get("stageEditorDeliveryNPCs") + " (" + Lang.get("stageEditorNoItemsSet") + ")\n";
if (context.getSessionData(pref + CK.S_DELIVERY_MESSAGES) == null) {
text += ChatColor.BLUE + "3 - " + Lang.get("stageEditorDeliveryMessages") + " (" + Lang.get("noneSet") + ")\n";
text += ChatColor.BLUE + "" + ChatColor.BOLD + "3" + ChatColor.RESET + ChatColor.BLUE + " - " + Lang.get("stageEditorDeliveryMessages") + " (" + Lang.get("noneSet") + ")\n";
} else {
text += ChatColor.BLUE + "3 - " + Lang.get("stageEditorDeliveryMessages") + "\n";
text += ChatColor.BLUE + "" + ChatColor.BOLD + "3" + ChatColor.RESET + ChatColor.BLUE + " - " + Lang.get("stageEditorDeliveryMessages") + "\n";
for (String s : getDeliveryMessages(context)) {
text += ChatColor.GRAY + " - " + ChatColor.AQUA + "\"" + s + "\"";
}
}
} else {
text += ChatColor.BLUE + "" + ChatColor.BOLD + "1" + ChatColor.RESET + ChatColor.YELLOW + " - " + Lang.get("stageEditorDeliveryAddItem") + "\n";
for (ItemStack is : getItems(context)) {
text += ChatColor.GRAY + " - " + ItemUtil.getDisplayString(is) + "\n";
}
text += ChatColor.BLUE + "" + ChatColor.BOLD + "1" + ChatColor.RESET + ChatColor.YELLOW + " - " + Lang.get("stageEditorDeliveryAddItem") + "\n";
if (context.getSessionData(pref + CK.S_DELIVERY_NPCS) == null) {
text += ChatColor.BLUE + "" + ChatColor.BOLD + "2" + ChatColor.RESET + ChatColor.YELLOW + " - " + Lang.get("stageEditorDeliveryNPCs") + " (" + Lang.get("noneSet") + ")\n";
} else {
@ -2039,9 +2039,9 @@ public class CreateStagePrompt extends FixedSetPrompt {
}
}
if (context.getSessionData(pref + CK.S_DELIVERY_MESSAGES) == null) {
text += ChatColor.BLUE + "3 - " + Lang.get("stageEditorDeliveryMessages") + " (" + Lang.get("noneSet") + ")\n";
text += ChatColor.BLUE + "" + ChatColor.BOLD + "3" + ChatColor.RESET + ChatColor.BLUE + " - " + Lang.get("stageEditorDeliveryMessages") + " (" + Lang.get("noneSet") + ")\n";
} else {
text += ChatColor.BLUE + "3 - " + Lang.get("stageEditorDeliveryMessages") + "\n";
text += ChatColor.BLUE + "" + ChatColor.BOLD + "3" + ChatColor.RESET + ChatColor.BLUE + " - " + Lang.get("stageEditorDeliveryMessages") + "\n";
for (String s : getDeliveryMessages(context)) {
text += ChatColor.GRAY + " - " + ChatColor.AQUA + "\"" + s + "\"\n";
}

View File

@ -15,6 +15,7 @@ package me.blackvein.quests.prompts;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@ -33,6 +34,7 @@ import org.bukkit.inventory.meta.ItemMeta;
import me.blackvein.quests.Quester;
import me.blackvein.quests.Quests;
import me.blackvein.quests.util.ItemUtil;
import me.blackvein.quests.util.Lang;
public class ItemStackPrompt extends FixedSetPrompt {
@ -44,20 +46,38 @@ public class ItemStackPrompt extends FixedSetPrompt {
// Stores enchantments in "tempEnchantments"
// Stores display name in "tempDisplay"
// Stores lore in "tempLore"
// Stores book title in "tempBookTitle"
// Stores book author in "tempBookAuthor"
// Stores book pages in "tempBookPages"
final Prompt oldPrompt;
public ItemStackPrompt(Prompt old) {
super("0", "1", "2", "3", "4", "5", "6", "7", "8");
super("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
oldPrompt = old;
}
@SuppressWarnings("unchecked")
@Override
public String getPromptText(ConversationContext cc) {
String menu = ChatColor.YELLOW + Lang.get("createItemTitle") + "\n";
LinkedHashMap<String, Object> map = null;
if (cc.getSessionData("tempName") != null) {
String stackData = getItemData(cc);
if (stackData != null) {
menu += stackData;
if (cc.getSessionData("tempMeta") != null) {
map = (LinkedHashMap<String, Object>) cc.getSessionData("tempMeta");
if (!map.isEmpty()) {
for (String key : map.keySet()) {
if (key.equals("pages")) {
List<String> pages = (List<String>) map.get(key);
menu += ChatColor.GRAY + "\u2515 " + ChatColor.DARK_GREEN + key + "=" + pages.size() + "\n";
} else {
menu += ChatColor.GRAY + "\u2515 " + ChatColor.DARK_GREEN + key + "=" + map.get(key) + "\n";
}
}
}
}
}
} else {
menu += "\n";
@ -69,8 +89,13 @@ public class ItemStackPrompt extends FixedSetPrompt {
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "4. " + ChatColor.RESET + "" + ChatColor.GOLD + Lang.get("itemCreateSetEnchs") + "\n";
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "5. " + ChatColor.RESET + "" + ChatColor.ITALIC + ChatColor.GOLD + Lang.get("itemCreateSetDisplay") + "\n";
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "6. " + ChatColor.RESET + "" + ChatColor.ITALIC + ChatColor.GOLD + Lang.get("itemCreateSetLore") + "\n";
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "7. " + ChatColor.RESET + "" + ChatColor.RED + Lang.get("cancel") + "\n";
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "8. " + ChatColor.RESET + "" + ChatColor.GREEN + Lang.get("done") + "\n";
if (map != null) {
if (!map.isEmpty()) {
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "7. " + ChatColor.RESET + "" + ChatColor.DARK_GREEN + Lang.get("itemCreateSetClearMeta") + "\n";
}
}
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "8. " + ChatColor.RESET + "" + ChatColor.RED + Lang.get("cancel") + "\n";
menu += ChatColor.YELLOW + "" + ChatColor.BOLD + "9. " + ChatColor.RESET + "" + ChatColor.GREEN + Lang.get("done") + "\n";
return menu;
}
@ -78,8 +103,9 @@ public class ItemStackPrompt extends FixedSetPrompt {
@Override
protected Prompt acceptValidatedInput(ConversationContext cc, String input) {
if (input.equalsIgnoreCase("0")) {
cc.setSessionData("tempMeta", null);
Player player = (Player) cc.getForWhom();
@SuppressWarnings("deprecation")
ItemStack is = player.getItemInHand();
if (is == null || is.getType().equals(Material.AIR)) {
player.sendMessage(ChatColor.RED + Lang.get("itemCreateNoItem"));
@ -108,11 +134,32 @@ public class ItemStackPrompt extends FixedSetPrompt {
lore.addAll(meta.getLore());
cc.setSessionData("tempLore", lore);
}
LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.putAll(meta.serialize());
/*for (String key : map.keySet()) {
String s = map.get(key).toString();
if (s.contains("minecraft:")) {
map.put(key, s.replace("minecraft:", "minecraft|"));
}
}*/
if (map.containsKey("lore")) {
map.remove("lore");
}
if (map.containsKey("display-name")) {
map.remove("display-name");
}
if (map != null && !map.isEmpty()) {
cc.setSessionData("tempMeta", map);
}
}
player.sendMessage(ChatColor.GREEN + Lang.get("itemCreateLoaded"));
return new ItemStackPrompt(oldPrompt);
}
} else if (input.equalsIgnoreCase("1")) {
cc.setSessionData("tempMeta", null);
return new NamePrompt();
} else if (input.equalsIgnoreCase("2")) {
if (cc.getSessionData("tempName") != null) {
@ -150,6 +197,14 @@ public class ItemStackPrompt extends FixedSetPrompt {
return new ItemStackPrompt(oldPrompt);
}
} else if (input.equalsIgnoreCase("7")) {
if (cc.getSessionData("tempName") != null && cc.getSessionData("tempAmount") != null) {
cc.setSessionData("tempMeta", null);
return new ItemStackPrompt(oldPrompt);
} else {
cc.getForWhom().sendRawMessage(ChatColor.RED + Lang.get("itemCreateNoNameAmount"));
return new ItemStackPrompt(oldPrompt);
}
} else if (input.equalsIgnoreCase("8")) {
cc.setSessionData("tempStack", null);
cc.setSessionData("tempName", null);
cc.setSessionData("tempAmount", null);
@ -157,7 +212,8 @@ public class ItemStackPrompt extends FixedSetPrompt {
cc.setSessionData("tempEnchantments", null);
cc.setSessionData("tempDisplay", null);
cc.setSessionData("tempLore", null);
} else if (input.equalsIgnoreCase("8")) {
cc.setSessionData("tempMeta", null);
} else if (input.equalsIgnoreCase("9")) {
if (cc.getSessionData("tempName") != null && cc.getSessionData("tempAmount") != null) {
String name = (String) cc.getSessionData("tempName");
int amount = (Integer) cc.getSessionData("tempAmount");
@ -181,11 +237,18 @@ public class ItemStackPrompt extends FixedSetPrompt {
lore.add(ChatColor.translateAlternateColorCodes('&', line));
}
}
ItemStack stack = new ItemStack(Material.matchMaterial(name), amount);
ItemMeta meta = stack.getItemMeta();
if (data != -1) {
stack.setDurability((short) data);
}
ItemMeta meta = stack.getItemMeta();
if ((Map<String, Object>) cc.getSessionData("tempMeta") != null) {
meta = ItemUtil.deserializeItemMeta(meta.getClass(), (Map<String, Object>) cc.getSessionData("tempMeta"));
}
if (enchs != null) {
for (Entry<Enchantment, Integer> e : enchs.entrySet()) {
meta.addEnchant(e.getKey(), e.getValue(), true);
@ -197,7 +260,9 @@ public class ItemStackPrompt extends FixedSetPrompt {
if (lore != null) {
meta.setLore(lore);
}
stack.setItemMeta(meta);
cc.setSessionData("tempStack", stack);
cc.setSessionData("newItem", Boolean.TRUE);
} else {
@ -477,4 +542,6 @@ public class ItemStackPrompt extends FixedSetPrompt {
return null;
}
}
}

View File

@ -12,12 +12,19 @@
package me.blackvein.quests.util;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.configuration.serialization.DelegateDeserialization;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
@ -95,8 +102,12 @@ public class ItemUtil {
ItemStack stack = null;
String[] args = data.split(":");
ItemMeta meta = null;
Map<Enchantment, Integer> enchs = new HashMap<Enchantment, Integer>();
String display = null;
LinkedList<String> lore = new LinkedList<String>();
for (String arg : args) {
LinkedHashMap<String, Object> extra = new LinkedHashMap<String, Object>();
for (String targ : args) {
String arg = targ.replace("minecraft|", "minecraft:");
if (arg.startsWith("name-")) {
try {
stack = new ItemStack(Material.matchMaterial(arg.substring(5).toUpperCase()));
@ -110,23 +121,56 @@ public class ItemUtil {
} else if (arg.startsWith("data-")) {
stack.setDurability(Short.parseShort(arg.substring(5)));
} else if (arg.startsWith("enchantment-")) {
String[] enchs = arg.substring(12).split(" ");
String[] temp = arg.substring(12).split(" ");
try {
Enchantment e = Quests.getEnchantment(enchs[0]);
meta.addEnchant(e, Integer.parseInt(enchs[1]), true);
enchs.put(Quests.getEnchantment(temp[0]), Integer.parseInt(temp[1]));
} catch (IllegalArgumentException e) {
Bukkit.getLogger().severe("[Quests] The enchantment name \'" + enchs[0] + "\' is invalid. Make sure quests.yml is UTF-8 encoded");
Bukkit.getLogger().severe("[Quests] The enchantment name \'" + temp[0] + "\' is invalid. Make sure quests.yml is UTF-8 encoded");
return null;
}
} else if (arg.startsWith("displayname-")) {
meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', arg.substring(12)));
display = ChatColor.translateAlternateColorCodes('&', arg.substring(12));
} else if (arg.startsWith("lore-")) {
lore.add(ChatColor.translateAlternateColorCodes('&', arg.substring(5)));
} else if (arg.contains("-")) {
int dash = arg.lastIndexOf('-');
String key = arg.substring(0, dash);
String value = arg.substring(dash + 1);
int i = -1;
try {
// Num such as book generation
i = Integer.valueOf(value);
} catch (NumberFormatException e) {
// Do nothing
}
if (i > -1) {
extra.put(key, i);
} else if (value.startsWith("[") && value.endsWith("]")) {
// Map such as book pages
List<String> pages = Arrays.asList(value.split(", "));
extra.put(key, pages);
} else {
extra.put(key, value);
}
} else {
return null;
}
}
if (lore.isEmpty() == false) {
if (!extra.isEmpty()) {
meta = ItemUtil.deserializeItemMeta(meta.getClass(), (Map<String, Object>) extra);
}
if (!enchs.isEmpty()) {
for (Enchantment e : enchs.keySet()) {
meta.addEnchant(e, enchs.get(e), true);
}
}
if (display != null) {
meta.setDisplayName(display);
}
if (!lore.isEmpty()) {
meta.setLore(lore);
}
stack.setItemMeta(meta);
@ -141,7 +185,7 @@ public class ItemUtil {
* @param is ItemStack
* @return formatted string, or null if invalid stack
*/
public static String serialize(ItemStack is) {
public static String serializeItemStack(ItemStack is) {
String serial;
if (is == null) {
return null;
@ -166,9 +210,27 @@ public class ItemUtil {
serial += ":lore-" + s;
}
}
LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
map.putAll(meta.serialize());
if (map.containsKey("lore")) {
map.remove("lore");
}
if (map.containsKey("display-name")) {
map.remove("display-name");
}
for (String key : map.keySet()) {
serial += ":" + key + "-" + map.get(key).toString().replace("minecraft:", "minecraft|");
}
}
return serial;
}
public static ItemMeta deserializeItemMeta(Class<? extends ItemMeta> itemMetaClass, Map<String, Object> args) {
DelegateDeserialization delegate = itemMetaClass.getAnnotation(DelegateDeserialization.class);
return (ItemMeta) ConfigurationSerialization.deserializeObject(args, delegate.value());
}
public static String getDisplayString(ItemStack is) {
String text;

View File

@ -148,23 +148,23 @@ public class QuestMob {
string += "::amounts-" + spawnAmounts;
}
if (inventory[0] != null) {
string += "::hand-" + ItemUtil.serialize(inventory[0]);
string += "::hand-" + ItemUtil.serializeItemStack(inventory[0]);
string += "::hand_drop-" + dropChances[0];
}
if (inventory[1] != null) {
string += "::boots-" + ItemUtil.serialize(inventory[1]);
string += "::boots-" + ItemUtil.serializeItemStack(inventory[1]);
string += "::boots_drop-" + dropChances[1];
}
if (inventory[2] != null) {
string += "::leggings-" + ItemUtil.serialize(inventory[2]);
string += "::leggings-" + ItemUtil.serializeItemStack(inventory[2]);
string += "::leggings_drop-" + dropChances[2];
}
if (inventory[3] != null) {
string += "::chest-" + ItemUtil.serialize(inventory[3]);
string += "::chest-" + ItemUtil.serializeItemStack(inventory[3]);
string += "::chest_drop-" + dropChances[3];
}
if (inventory[4] != null) {
string += "::helmet-" + ItemUtil.serialize(inventory[4]);
string += "::helmet-" + ItemUtil.serializeItemStack(inventory[4]);
string += "::helmet_drop-" + dropChances[4];
}
return string;

View File

@ -546,6 +546,7 @@ itemCreateSetDurab: "Set durability"
itemCreateSetEnchs: "Add/clear enchantments"
itemCreateSetDisplay: "Set display name"
itemCreateSetLore: "Set lore"
itemCreateSetClearMeta: "Clear extra data"
itemCreateEnterName: "Enter an item name, <cancel>"
itemCreateEnterAmount: "Enter item amount (max. 64), <cancel>"
itemCreateEnterDurab: "Enter item durability, <clear>, <cancel>"
@ -572,6 +573,9 @@ dateCreateEnterSecond: "Enter a second (max. 59), <cancel>"
dateCreateEnterOffset: "Enter a UTC time offset (max. 14), <cancel>"
dateCreateEnterZone: "Enter a UTC time zone, <cancel>"
dateCreateNoYearAmount: "You must set a year first!"
bookCreateEnterTitle: "Enter a book title, <cancel>"
bookCreateEnterAuthor: "Enter a book author, <cancel>"
bookCreateEnterPages: "Enter a list of page contents, <semicolon>, <cancel>"
questTitle: "-- <quest> --"
questObjectivesTitle: "---(<quest>)---"
questCompleteTitle: '**QUEST COMPLETE: <quest>**'
@ -617,6 +621,7 @@ topQuestersTitle: "- Top <number> Questers -"
createItemTitle: "- Create Item -"
dateTimeTitle: "- Date & Time -"
timeZoneTitle: "- Time Zones -"
bookMetaTitle: "- Book Meta -"
enchantmentsTitle: "- Enchantments -"
questGUITitle: "- GUI Item Display -"
questRegionTitle: "- Quest Region -"