package de.cowtipper.cowlection.config;
import com.google.common.collect.Maps;
import de.cowtipper.cowlection.Cowlection;
import de.cowtipper.cowlection.command.MooCommand;
import de.cowtipper.cowlection.command.TabCompletableCommand;
import de.cowtipper.cowlection.config.gui.MooConfigGui;
import de.cowtipper.cowlection.config.gui.MooConfigPreview;
import de.cowtipper.cowlection.data.DataHelper;
import de.cowtipper.cowlection.partyfinder.Rule;
import de.cowtipper.cowlection.util.MooChatComponent;
import de.cowtipper.cowlection.util.Utils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.SoundCategory;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.command.ICommand;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.Util;
import net.minecraftforge.client.ClientCommandHandler;
import net.minecraftforge.common.ForgeModContainer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.client.event.ConfigChangedEvent;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.lwjgl.input.Keyboard;
import java.io.File;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.regex.Pattern;
/**
* Mod configuration
*
* Based on TheGreyGhost's MinecraftByExample
*
* @see ForgeModContainer
*/
public class MooConfig {
// Category: General
private static String configGuiExplanations;
public static boolean hasOpenedConfigGui;
public static String mooCmdAlias;
public static boolean fixReplyCmd;
public static boolean enableCopyInventory;
private static String wailaLevelOfDetail;
private static String copyOrSaveWailaAndInventoryData;
public static String[] tabCompletableNamesCommands;
private static final String CATEGORY_LOGS_SEARCH = "logssearch";
public static String[] logsDirs;
private static String defaultStartDate;
private static int maxLogFileSize;
private static int maxLatestLogFileSize;
// Category: Notifications
public static boolean doUpdateCheck;
public static boolean showBestFriendNotifications;
public static boolean enableBestFriendNotificationSound;
public static boolean showFriendNotifications;
public static boolean showGuildNotifications;
public static boolean doBestFriendsOnlineCheck;
// Category: SkyBlock
private static String enableSkyBlockOnlyFeatures;
public static int notifyFreshServer;
public static int notifyOldServer;
public static boolean notifyServerAge;
public static boolean chestAnalyzerShowBazaarItems;
private static String chestAnalyzerUseBazaarPrices;
public static boolean chestAnalyzerShowLowestBinItems;
public static boolean chestAnalyzerShowNpcItems;
public static boolean chestAnalyzerShowNoPriceItems;
public static boolean chestAnalyzerShowCommandUsage;
public static int tooltipToggleKeyBinding;
private static String tooltipItemAge;
public static boolean tooltipItemAgeShortened;
private static String tooltipItemTimestamp;
private static String showPetExp;
private static String numeralSystem;
private static String tooltipAuctionHousePriceEach;
public static String[] tooltipAuctionHousePriceEachEnchantments;
private static String auctionHouseMarkEndedAuctions;
public static String bazaarSellAllOrder;
public static String bazaarSellAllOrderAscDesc;
public static boolean bazaarShowItemsLeft;
private static String bazaarConnectGraphsNodes;
public static int bazaarConnectGraphsLineWidth;
public static String bestiaryOverviewOrder;
private String[] bestiaryOverviewOrderDefaultValues;
private static int lookupWikiKeyBinding;
private static String lookupWikiPreferredWiki;
private static int lookupPriceKeyBinding;
public static boolean lookupItemDirectly;
// Category: SkyBlock Dungeons
private static String showItemQualityAndFloor;
private static String dungItemQualityPos;
public static boolean dungItemQualityShortenNonRandomized;
public static boolean dungItemHideGearScore;
public static int dungItemToolTipToggleKeyBinding;
public static boolean dungSendPerformanceOnDeath;
public static boolean dungSendPerformanceOnEndScreen;
public static boolean dungOverlayEnabled;
public static int dungOverlayPositionX;
public static int dungOverlayPositionY;
public static int dungOverlayGuiScale;
private static String dungOverlayTextBorder;
private static String dungPartyFinderPlayerLookup;
public static boolean dungPartyFullLookup;
public static boolean dungPartyFinderPartyLookup;
public static boolean dungPartyFinderOverlayDrawBackground;
public static boolean dungPartiesSize;
public static int dungDungeonReqMin;
public static int dungClassMin;
private static String dungMarkPartiesWithArcher;
private static String dungMarkPartiesWithBerserk;
private static String dungMarkPartiesWithHealer;
private static String dungMarkPartiesWithMage;
private static String dungMarkPartiesWithTank;
public static boolean dungPartyFinderRuleEditorSimplified;
public static boolean dungPartyFinderRuleEditorShowOpenButton;
public static boolean dungSendWrongFloorWarning;
private static Configuration cfg = null;
private static final List configCategories = new ArrayList<>();
private final Cowlection main;
private Property propMooCmdAlias;
private Property propTabCompletableNamesCommands;
private Property propTooltipAuctionHousePriceEachEnchantments;
private List logSearchProperties;
public MooConfig(Cowlection main, Configuration configuration) {
this.main = main;
cfg = configuration;
// config version history: null > 1 > 0.12.0 > 0.13.0-pre > 2
String oldLoadedConfigVersion = cfg.getLoadedConfigVersion();
boolean configVersionChanged = oldLoadedConfigVersion == null || !oldLoadedConfigVersion.equals(cfg.getDefinedConfigVersion());
if (configVersionChanged) {
updateConfigPreInit(oldLoadedConfigVersion);
}
initConfig();
if (configVersionChanged) {
updateConfigPostInit(oldLoadedConfigVersion);
}
}
private void updateConfigPreInit(String oldVersion) {
if (oldVersion == null) {
// config of Cowlection v1.8.9-0.10.2 and older
// leave log search settings as is
if (cfg.hasCategory(Configuration.CATEGORY_CLIENT)) {
// copy old 'moo' value to new, separate config
if (cfg.hasKey(Configuration.CATEGORY_CLIENT, "moo")) {
String oldMoo = cfg.getString("moo", Configuration.CATEGORY_CLIENT, "00000000-0000-0000-0000-000000000000", "Temporary config entry, should be deleted automatically.", Utils.VALID_UUID_PATTERN);
if (StringUtils.isNotEmpty(oldMoo) && Utils.isValidUuid(oldMoo)) {
// save into new cfg:
main.getMoo().setMooIfValid(oldMoo, false);
}
}
// delete client category (no longer used)
ConfigCategory oldClientCategory = cfg.getCategory(Configuration.CATEGORY_CLIENT);
cfg.removeCategory(oldClientCategory);
}
cfg.save();
}
}
private void updateConfigPostInit(String oldVersion) {
boolean changedSomething = false;
if ("1".equals(oldVersion)) {
// config of Cowlection v1.8.9-0.12.0 and older
ConfigCategory sbDungCategory = cfg.getCategory("skyblockdungeons");
if (sbDungCategory.containsKey("dungOverlayTextShadow")) {
boolean dungOverlayTextShadow = sbDungCategory.get("dungOverlayTextShadow").getBoolean();
if (!dungOverlayTextShadow) {
sbDungCategory.get("dungOverlayTextBorder").set("no border");
}
sbDungCategory.remove("dungOverlayTextShadow");
changedSomething = true;
}
for (String dungClass : new String[]{"Archer", "Berserk", "Healer", "Mage", "Tank"}) {
String configKey = "dungFilterPartiesWith" + dungClass + "Dupes";
if (sbDungCategory.containsKey(configKey)) {
boolean filterPartiesWithX = sbDungCategory.get(configKey).getBoolean();
if (filterPartiesWithX) {
String configKeyNew = "dungMarkPartiesWith" + dungClass;
sbDungCategory.get(configKeyNew).set("if dupe: " + EnumChatFormatting.GOLD + "⬛");
}
sbDungCategory.remove(configKey);
changedSomething = true;
}
}
} else if ("0.12.0".equals(oldVersion) || "0.13.0-pre".equals(oldVersion)) { // matches config versions pre mod version 0.14.0
ConfigCategory sbDungCategory = cfg.getCategory("skyblockdungeons");
for (String dungClass : new String[]{"Archer", "Berserk", "Healer", "Mage", "Tank"}) {
String configKey = "dungMarkPartiesWith" + dungClass;
if (sbDungCategory.containsKey(configKey)) {
String markPartiesWithXOld = sbDungCategory.get(configKey).getString();
String markPartiesWithXNew;
switch (markPartiesWithXOld) {
case "always":
markPartiesWithXNew = "always: " + EnumChatFormatting.GOLD + "⬛";
break;
case "if duplicated":
markPartiesWithXNew = "if dupe: " + EnumChatFormatting.GOLD + "⬛";
break;
default:
continue;
}
sbDungCategory.get(configKey).set(markPartiesWithXNew);
changedSomething = true;
}
}
Property dungMarkPartiesWithCarry = sbDungCategory.get("dungMarkPartiesWithCarry");
if (dungMarkPartiesWithCarry != null) {
if (!"do not mark".equals(dungMarkPartiesWithCarry.getString())) {
for (Rule rule : main.getPartyFinderRules().getRules()) {
if (rule.getTriggerText().contains("carr")) {
rule.setPartyType(DataHelper.PartyType.getByFancyString(dungMarkPartiesWithCarry.getString()));
rule.setEnabled(true);
}
}
}
sbDungCategory.remove("dungMarkPartiesWithCarry");
changedSomething = true;
}
Property dungMarkPartiesWithHyperion = sbDungCategory.get("dungMarkPartiesWithHyperion");
if (dungMarkPartiesWithHyperion != null) {
if (!"do not mark".equals(dungMarkPartiesWithHyperion.getString())) {
for (Rule rule : main.getPartyFinderRules().getRules()) {
if (rule.getTriggerText().contains("hyp")) {
rule.setPartyType(DataHelper.PartyType.getByFancyString(dungMarkPartiesWithHyperion.getString()));
rule.setEnabled(true);
}
}
}
sbDungCategory.remove("dungMarkPartiesWithHyperion");
changedSomething = true;
}
}
// else if (MathHelper.parseIntWithDefault(oldVersion, 9999) < newVersion) { // matches all versions >= 2
if (changedSomething) {
cfg.save();
syncFromFile();
}
}
private void initConfig() {
syncFromFile();
MinecraftForge.EVENT_BUS.register(new ConfigEventHandler());
}
/**
* Load the configuration values from the configuration file
*/
private void syncFromFile() {
syncConfig(true, true, true);
}
/**
* Save the GUI-altered values to disk
*/
public void syncFromGui() {
syncConfig(false, true, true);
}
/**
* Save the GUI-altered values to the properties; don't save to disk - only memory
*/
public void syncFromGuiWithoutSaving() {
syncConfig(false, true, false);
}
/**
* Save the Configuration variables (fields) to disk
*/
public void syncFromFields() {
syncConfig(false, false, true);
}
/**
* Synchronise the three copies of the data
* 1) loadConfigFromFile && readFieldsFromConfig -> initialise everything from the disk file
* 2) !loadConfigFromFile && readFieldsFromConfig -> copy everything from the config file (altered by GUI)
* 3) !loadConfigFromFile && !readFieldsFromConfig -> copy everything from the native fields
*
* @param loadConfigFromFile if true, load the config field from the configuration file on disk
* @param readFieldsFromConfig if true, reload the member variables from the config field
* @param saveToFile if true, save changes to config file
*/
@SuppressWarnings("DuplicatedCode")
private void syncConfig(boolean loadConfigFromFile, boolean readFieldsFromConfig, boolean saveToFile) {
if (loadConfigFromFile) {
cfg.load();
}
// reset previous entries
configCategories.clear();
// Category: General
MooConfigCategory configCat = new MooConfigCategory("General", "general");
configCategories.add(configCat);
// Sub-Category: Cowlection config gui
MooConfigCategory.SubCategory subCat = configCat.addSubCategory("Cowlection config gui");
subCat.addExplanations("Display of the explanations for each sub-section marked with §2❢§r",
" ‣ " + EnumChatFormatting.YELLOW + "as tooltip" + EnumChatFormatting.RESET + " = tooltip when hovering over sub-category heading",
" ‣ " + EnumChatFormatting.YELLOW + "as text" + EnumChatFormatting.RESET + " = below each sub-category heading",
" ‣ " + EnumChatFormatting.YELLOW + "hidden" + EnumChatFormatting.RESET + " = ");
Property propConfigGuiExplanations = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"configGuiExplanations", "as tooltip", "Display config settings explanations",
new String[]{"as tooltip", "as text", "hidden"}));
// (not visible in config gui: has opened config? or: knows how to move the dungeon overlay?)
Property propHasOpenedConfigGui = cfg.get(configCat.getConfigName(),
"hasOpenedConfigGui", false, "Already opened config gui?")
.setShowInGui(false);
// Sub-Category: API settings
subCat = configCat.addSubCategory("API settings");
subCat.addExplanations("Some features use the official Hypixel API and therefore require your API key.",
"Use " + EnumChatFormatting.YELLOW + "/moo apikey " + EnumChatFormatting.RESET + "to see how to request a new API key from Hypixel",
"The API key is stored " + EnumChatFormatting.ITALIC + "locally " + EnumChatFormatting.ITALIC + "on your computer.");
subCat.addConfigEntry(main.getMoo().getPropIsMooValid());
// Sub-Category: Command settings
subCat = configCat.addSubCategory("Command settings");
propMooCmdAlias = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"mooCmdAlias", "m", "Alias for /moo command")
.setValidationPattern(Pattern.compile("^[A-Za-z]*$")));
Property propFixReplyCmd = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"fixReplyCmd", true, "Auto-replace /r?"));
Property propEnableCopyInventory = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"enableCopyInventory", false, "Enable copy inventory with CTRL + C?"));
Property propWailaLevelOfDetail = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"wailaLevelOfDetail", "main info", "Level of detail of /moo waila", new String[]{"main info", "all info"}));
Property propCopyOrSaveWailaAndInventoryData = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"copyOrSaveWailaAndInventoryData", "to clipboard", "Copy or save waila and inventory data?", new String[]{"to clipboard", "save file"}));
// Sub-Category: Tab-completable names in commands
subCat = configCat.addSubCategory("Tab-completable usernames");
subCat.addExplanations("For certain commands you can use " + EnumChatFormatting.YELLOW + "TAB " + EnumChatFormatting.RESET + "to autocomplete player names",
EnumChatFormatting.UNDERLINE + "Uses player names from:",
" ‣ Guild and Party chat",
" ‣ Party and game (duels) invites",
" ‣ SkyBlock Dungeon party finder: when a player joins the group",
" ‣ Online best friends (if the best friend online checker is enabled)");
propTabCompletableNamesCommands = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"tabCompletableNamesCommands", new String[]{"p", "ah", "ignore", "msg", "tell", "w", "boop", "profile", "friend", "friends"}, "List of commands with a Tab-completable username argument.")
.setValidationPattern(Pattern.compile("^[A-Za-z]+$")));
// Sub-Category: Other settings
subCat = configCat.addSubCategory("Other settings");
subCat.addExplanations("Other settings that are located in other GUIs");
Property propLogsDirs = subCat.addConfigEntry(cfg.get(CATEGORY_LOGS_SEARCH,
"logsDirs", resolveDefaultLogsDirs(),
"Directories with Minecraft log files"));
Property propDefaultStartDate = subCat.addConfigEntry(cfg.get(CATEGORY_LOGS_SEARCH,
"defaultStartDate", "3", "Default start date (a number means X months ago, alternatively a fixed date à la yyyy-mm-dd can be used)"))
.setValidationPattern(Pattern.compile("^[1-9][0-9]{0,2}|(2[0-9]{3}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))$"));
Property propMaxLogFileSize = subCat.addConfigEntry(cfg.get(CATEGORY_LOGS_SEARCH,
"maxLogFileSize", 2048, "Max log file size (in KB)?", 50, 10000));
Property propMaxLatestLogFileSize = subCat.addConfigEntry(cfg.get(CATEGORY_LOGS_SEARCH,
"maxLatestLogFileSize", 50000, "Max latest.log file size (in KB)?", 50, 200000));
logSearchProperties = new ArrayList<>();
logSearchProperties.add(propLogsDirs);
logSearchProperties.add(propDefaultStartDate);
logSearchProperties.add(propMaxLogFileSize);
logSearchProperties.add(propMaxLatestLogFileSize);
// Category: Notifications
configCat = new MooConfigCategory("Notifications", "notifications");
configCategories.add(configCat);
// Sub-Category: Mod update checker
subCat = configCat.addSubCategory("Mod update checker");
Property propDoUpdateCheck = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"doUpdateCheck", true, "Check for mod updates?"));
// Sub-Category: Login & Logout
subCat = configCat.addSubCategory("Login & Logout");
subCat.addExplanations("Hides selected login/logout notifications ",
"while still showing notifications of best friends (if enabled).",
"Add someone to the best friends list with " + EnumChatFormatting.YELLOW + "/moo add " + EnumChatFormatting.RESET);
Property propShowBestFriendNotifications = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"showBestFriendNotifications", true, "Set to true to receive best friends' login/logout messages, set to false hide them."),
new MooConfigPreview(new ChatComponentText("§a§lBest friend §a> §6Cow §r§ejoined.")));
Property propEnableBestFriendNotificationSound = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"enableBestFriendNotificationSound", false, "Set to true to play a notification sound when a best friend comes online"),
new MooConfigPreview("random.pop", Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.MASTER), 1));
Property propShowFriendNotifications = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"showFriendNotifications", true, "Set to true to receive friends' login/logout messages, set to false hide them."),
new MooConfigPreview(new ChatComponentText("§aFriend > §r§aBob §ejoined.")));
Property propShowGuildNotifications = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"showGuildNotifications", true, "Set to true to receive guild members' login/logout messages, set to false hide them."),
new MooConfigPreview(new ChatComponentText("§2Guild > §r§7Herobrian §eleft.")));
// Sub-Category: Best friends online status
subCat = configCat.addSubCategory("Best friend online checker");
subCat.addExplanations("Check which best friends are online when you join the server.",
"About once a day, a check for new name changes is also performed automatically.");
IChatComponent spacer = new MooChatComponent(", ").green();
Property propDoBestFriendsOnlineCheck = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"doBestFriendsOnlineCheck", true, "Set to true to check best friends' online status when joining a server, set to false to disable."),
new MooConfigPreview(new MooChatComponent("§a⬤ Online best friends (§24§a/§216§a): ")
.appendSibling(MooConfigPreview.createDemoOnline("Alice", "Housing", "1 hour 13 minutes 37 seconds")).appendSibling(spacer)
.appendSibling(MooConfigPreview.createDemoOnline("Bob", "Build Battle", "2 hours 13 minutes 37 seconds")).appendSibling(spacer)
.appendSibling(MooConfigPreview.createDemoOnline("Cow", "SkyBlock", "13 minutes 37 seconds")).appendSibling(spacer)
.appendSibling(MooConfigPreview.createDemoOnline("Herobrian", "Murder Mystery", "13 hours 33 minutes 37 seconds"))));
// Category: SkyBlock
configCat = new MooConfigCategory("SkyBlock", "skyblock");
configCategories.add(configCat);
// Sub-Category: SkyBlock-only features
subCat = configCat.addSubCategory("SkyBlock-only features");
Property propEnableSkyBlockOnlyFeatures = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"enableSkyBlockOnlyFeatures", "on SkyBlock", "Enable SkyBlock-only features?", new String[]{"on SkyBlock", "always", "never"}));
// Sub-Category: Server age notifications
subCat = configCat.addSubCategory("Server age notifications");
subCat.addExplanations("Servers usually restart once they exceed " + EnumChatFormatting.YELLOW + "30-38 ingame days " + EnumChatFormatting.RESET + "(10-13 hours)",
"Use the command " + EnumChatFormatting.YELLOW + "/moo worldage " + EnumChatFormatting.RESET + "to check how long the current world is loaded.",
EnumChatFormatting.ITALIC + "Set a value to 0 to disable that notification.");
Property propNotifyFreshServer = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"notifyFreshServer", 1, "Notify when a world is loaded X ingame days", 0, 40));
Property propNotifyServerAge = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"notifyServerAge", true, "Show server age notifications?"));
// Sub-Category: Chest Analyzer (Bazaar, lowest BIN, & NPC prices)
subCat = configCat.addSubCategory("Chest Tracker & Analyzer (Bazaar, lowest BIN, & NPC prices)");
String analyzeCommand = "/moo analyzeChests";
subCat.addExplanations("Use " + EnumChatFormatting.YELLOW + analyzeCommand + EnumChatFormatting.RESET + " to start tracking chests on your island! " + EnumChatFormatting.GREEN + "Then you can...",
EnumChatFormatting.GREEN + " ❶ " + EnumChatFormatting.RESET + "add chests by opening them; deselect chests by Sneaking + Right Click.",
EnumChatFormatting.GREEN + " ❷ " + EnumChatFormatting.RESET + "use " + EnumChatFormatting.YELLOW + analyzeCommand + EnumChatFormatting.RESET + " again to run the chest analysis.",
EnumChatFormatting.GREEN + " ❸ " + EnumChatFormatting.RESET + "use " + EnumChatFormatting.YELLOW + analyzeCommand + " stop" + EnumChatFormatting.RESET + " to stop the chest tracker and clear current results.",
"",
EnumChatFormatting.GREEN + "Inside the GUI you can also:",
EnumChatFormatting.WHITE + " ‣ " + EnumChatFormatting.GOLD + "double click" + EnumChatFormatting.WHITE + ": highlight chests with selected item; hover over the chat message to see chest coords",
EnumChatFormatting.WHITE + " ‣ " + EnumChatFormatting.GOLD + "right click" + EnumChatFormatting.WHITE + ": exclude item from sell value calculation");
Property propChestAnalyzerShowBazaarItems = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"chestAnalyzerShowBazaarItems", true, "Show Bazaar items in Chest Tracker?"));
Property propChestAnalyzerUseBazaarPrices = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"chestAnalyzerUseBazaarPrices", "Instant-Sell", "Use Bazaar prices?", new String[]{"Instant-Sell", "Sell Offer"}));
Property propChestAnalyzerShowLowestBinItems = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"chestAnalyzerShowLowestBinItems", true, "Show lowest BIN items in Chest Tracker?"));
Property propChestAnalyzerShowNpcItems = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"chestAnalyzerShowNpcItems", true, "Show NPC items in Chest Tracker?"));
Property propChestAnalyzerShowNoPriceItems = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"chestAnalyzerShowNoPriceItems", false, "Show items without price in Chest Tracker?"));
Property propChestAnalyzerShowCommandUsage = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"chestAnalyzerShowCommandUsage", true, "Show command usage?"));
// Sub-Category: Tooltip enhancements
subCat = configCat.addSubCategory("Tooltip & GUI enhancements");
Property propTooltipToggleKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"tooltipToggleKeyBinding", Keyboard.KEY_LSHIFT, "Key to toggle tooltip"));
Map demoItemExtraAttributes = new HashMap<>();
demoItemExtraAttributes.put("new_years_cake", new NBTTagInt(1));
demoItemExtraAttributes.put("originTag", new NBTTagString("REWARD_NEW_YEARS_CAKE_NPC"));
demoItemExtraAttributes.put("id", new NBTTagString("NEW_YEAR_CAKE"));
demoItemExtraAttributes.put("uuid", new NBTTagString("64b3a60b-74f2-4ebd-818d-d019c5b7f3e0"));
demoItemExtraAttributes.put("timestamp", new NBTTagString("6/16/19 5:05 PM"));
MooConfigPreview nonStackableItemPreview = new MooConfigPreview(MooConfigPreview.createDemoItem("cake", "§dNew Year Cake", new String[]{"§7Given to every player as a", "§7celebration for the 1st SkyBlock", "§7year!", "", "§d§lSPECIAL"}, demoItemExtraAttributes));
Property propTooltipItemAge = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"tooltipItemAge", "always", "Show item age", new String[]{"always", "key press", "never"}),
nonStackableItemPreview);
Property propTooltipItemAgeShortened = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"tooltipItemAgeShortened", true, "Shorten item age?"));
Property propTooltipItemTimestamp = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"tooltipItemTimestamp", "key press", "Show item creation date", new String[]{"always", "key press", "never"}));
Property propShowPetExp = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"showPetExp", "always", "Show pet exp", new String[]{"always", "key press", "never"}));
Property propNumeralSystem = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"numeralSystem", "Arabic: 1, 4, 10", "Use Roman or Arabic numeral system?", new String[]{"Arabic: 1, 4, 10", "Roman: I, IV, X"}));
Map demoAhItemExtraAttributes = new HashMap<>();
demoAhItemExtraAttributes.put("id", new NBTTagString("BEACON"));
ItemStack demoAhItem = MooConfigPreview.createDemoItem("beacon", "§764x §fB§8e§facon Block", new String[]{"§f§lCOMMON", "§8§m-----------------", "§7Seller: §6[MVP§0++§6] Enlightener", "§7Buy it now: §63,900,000 coins", "", "§7Ends in: §e13h 33m 37s", "", "§eDon't click to inspect!"}, demoAhItemExtraAttributes);
demoAhItem.stackSize = 64;
MooConfigPreview ahItemPreview = new MooConfigPreview(demoAhItem);
Property propTooltipAuctionHousePriceEach = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"tooltipAuctionHousePriceEach", "always", "Add price per item if multiple items are bought or sold", new String[]{"always", "key press", "never"}), ahItemPreview);
propTooltipAuctionHousePriceEachEnchantments = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"tooltipAuctionHousePriceEachEnchantments", new String[]{"overload", "rejuvenate"}, "Price per lvl 1 book enchantment")
.setValidationPattern(Pattern.compile("^[A-Za-z_ -]+$")));
Property propAuctionHouseMarkEndedAuctions = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"auctionHouseMarkEndedAuctions", "a letter", "Mark ended auctions", new String[]{"a letter", "a word", "disabled"}));
Property propBazaarSellAllOrder = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"bazaarSellAllOrder", "price (sum)", "Bazaar: sell all order", new String[]{"price (sum)", "item amount", "unordered", "price (each)"}),
new MooConfigPreview(MooConfigPreview.createDemoItem("chest", "§aSell Inventory Now", new String[]{"§7Instantly sell anything in", "§7your inventory that can be", "§7sold on the Bazaar.", "", " §a1§7x §aEnchanted Leather §7for §65,263.1 coins", " §a42§7x §fLeather §7for §6436.8 coins", " §a2§7x §fRabbit Hide §7for §642.0 coins", " §a79§7x §fRaw Beef §7for §6450.3 coins", " §a16§7x §aEnchanted Raw Beef §7for §69,867.2 coins", "", "§7You earn: §615,698 coins", "", "§eClick to sell!"}, Collections.emptyMap())));
Property propBazaarSellAllOrderAscDesc = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"bazaarSellAllOrderAscDesc", "low → high", "Bazaar: sell all order asc/desc", new String[]{"low → high", "high → low"}));
Property propBazaarShowItemsLeft = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"bazaarShowItemsLeft", true, "Bazaar: show items left"));
MooConfigPreview bazaarGraphPreview = new MooConfigPreview(MooConfigPreview.createDemoItem("paper", "§aBuy Price §731d §77d §e24h", new String[]{
"§7The price at which buy orders have been filled.", "",
"§r┌----------------------------------------------┐", "§r│§66. 1k§r+§bxxxxxx§8·································§bxx§r│",
"§r│§8····§r│§8······································§bx§8··§r│", "§r│§66. 1k§r+§8·····§bx§8···················§bx§8·······§bxxxxx§8···§r│",
"§r│§8····§r│§8···············§bx§8········§bxxxxxxxxx§8········§r│", "§r│§8··§66k§r+§8··············§bx§8····§bxx§8··§bx§8·················§r│",
"§r│§8····§r│§8············§bx§8··§bxxxx§8·§bxxx§8··················§r│", "§r│§8··§66k§r+§8······§bx§8·§bxxxx§8·§bx§8···························§r│",
"§r│§8····§r│§8·······§bx§8·································§r│", "§r│§8··§66k§r+---------+----------+---------+---------+│",
"§r│§8····§r24h§8······§r18h§8········§r12h§8·······§r6h§8·······§rnow│", "§r└----------------------------------------------┘"}, Maps.newHashMap()));
Property propBazaarConnectGraphsNodes = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"bazaarConnectGraphsNodes", "always", "Bazaar: connect the graph nodes", new String[]{"always", "key press", "never"}),
bazaarGraphPreview);
Property propBazaarConnectGraphsLineWidth = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"bazaarConnectGraphsLineWidth", 3, "Line width of bazaar graph", 1, 10));
bestiaryOverviewOrderDefaultValues = new String[]{"fewest kills", "lowest %", "hidden"};
MooConfigPreview bestiaryOverviewPreview = new MooConfigPreview(MooConfigPreview.createDemoItem("wheat", "§a§3The Barn", new String[]{
"§7View all of the mobs that you've",
"§7found and killed in §3The Barn§7.",
"",
"§7Families Found: §e75§6%", "§3---------------§f----- §b3§3/§b4"}, Maps.newHashMap()));
Property propBestiaryOverviewOrder = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"bestiaryOverviewOrder", "fewest kills", "Order of the Bestiary overview?", bestiaryOverviewOrderDefaultValues),
bestiaryOverviewPreview);
// Sub-Category: Item lookup
subCat = configCat.addSubCategory("Item lookup");
subCat.addExplanations("Lookup item prices or wiki articles for any SkyBlock item in any inventory.");
Property propLookupWikiKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"lookupWikiKeyBinding", Keyboard.KEY_I, "Key to lookup wiki"));
Property propLookupWikiPreferredWiki = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"lookupWikiPreferredWiki", "unofficial", "Prefer official or unofficial wiki?", new String[]{"unofficial", "official"}));
Property propLookupPriceKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"lookupPriceKeyBinding", Keyboard.KEY_P, "Key to lookup item price"));
Property propLookupItemDirectly = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"lookupItemDirectly", true, "Open website directly?"));
// Category: SkyBlock Dungeons
configCat = new MooConfigCategory("SkyBlock Dungeons", "skyblockdungeons");
configCat.setMenuDisplayName("SB Dungeons");
configCategories.add(configCat);
// Sub-Category: Tooltip enhancements
subCat = configCat.addSubCategory("Dungeon item tooltip enhancements");
subCat.addExplanations("Shows " + EnumChatFormatting.YELLOW + "item quality " + EnumChatFormatting.RESET + "and " + EnumChatFormatting.YELLOW + "dungeon floor" + EnumChatFormatting.RESET + " (if enabled)",
"",
"Hold left " + EnumChatFormatting.YELLOW + "SHIFT " + EnumChatFormatting.RESET + "while hovering over a dungeon item,",
"to remove stats from reforges and essences (✪)",
"which normally makes the comparison of dungeon items difficult.",
"Instead, the tooltip shows...",
" ‣ base/default stats " + EnumChatFormatting.GRAY + "(outside dungeons; 1st value - usually red or green)",
" ‣ stats inside dungeons " + EnumChatFormatting.GRAY + "(including dungeon level stat boost, but without essences [₀ₓ✪])",
" ‣ stats inside dungeons with 5x essence upgrades " + EnumChatFormatting.GRAY + "(₅ₓ✪)");
Property propShowItemQualityAndFloor = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"showItemQualityAndFloor", "always", "show item quality + obtained floor?", new String[]{"always", "key press", "never"}));
Property propDungItemQualityPos = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungItemQualityPos", "top", "Position of item quality in tooltip", new String[]{"top", "bottom"}),
new MooConfigPreview(
MooConfigPreview.createDungeonItem("light", "7/17/20 7:22 PM", "§7Gear Score: §d336 §8(526)", "§7Crit Chance: §c+5% §9(Light +2%)", "§7Crit Damage: §c+30% §9(Light +4%) §8(+48.9%)", "§7Bonus Attack Speed: §c+4% §9(Light +4%)", "", "§7Health: §a+126 HP §9(Light +15 HP) §8(+205.38 HP)", "§7Defense: §a+76 §9(Light +4) §8(+123.88)", "§7Speed: §a+4 §9(Light +4) §8(+6.52)", "", "§9Growth V, §9Protection V", "§9Thorns III", "", "§7Increase the damage you deal", "§7with arrows by §c5%§7.", "", "§6Full Set Bonus: Skeleton Soldier", "§7Increase the damage you deal", "§7with arrows by an extra §c25%§7.", "", "§aPerfect 52500 / 52500", "§5§lEPIC DUNGEON LEGGINGS"),
MooConfigPreview.createDungeonItem("clean", "7/11/20 12:27 PM", "§7Gear Score: §d359 §8(561)", "§7Crit Chance: §c+11% §9(Clean +8%)", "§7Crit Damage: §c+26% §8(+42.38%)", "", "§7Health: §a+126 HP §9(Clean +15 HP) §8(+205.38 HP)", "§7Defense: §a+87 §9(Clean +15) §8(+141.81)", "", "§9Growth V, §9Protection V", "§9Thorns III", "", "§7Increase the damage you deal", "§7with arrows by §c5%§7.", "", "§6Full Set Bonus: Skeleton Soldier", "§7Increase the damage you deal", "§7with arrows by an extra §c25%§7.", "", "§aPerfect 52500 / 52500", "§5§lEPIC DUNGEON LEGGINGS")));
Property propDungItemQualityShortenNonRandomized = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungItemQualityShortenNonRandomized", false, "Shorten item quality for non-randomized items?"));
Property propDungItemHideGearScore = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungItemHideGearScore", false, "Hide Gear Score?"));
Property propDungItemToolTipToggleKeyBinding = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungItemToolTipToggleKeyBinding", Keyboard.KEY_LSHIFT, "Key to toggle dungeon item tooltip"));
// Sub-Category: Dungeon Performance Overlay
subCat = configCat.addSubCategory("Dungeon Performance Overlay");
subCat.addExplanations(EnumChatFormatting.UNDERLINE + "Keeps track of:",
" ‣ skill score " + EnumChatFormatting.GRAY + "(reduced by deaths and failed puzzles)",
" ‣ speed score " + EnumChatFormatting.GRAY + "(-2.2 points/minute when over 20 minutes)",
" ‣ bonus score " + EnumChatFormatting.GRAY + "(+1 [max 5] for each destroyed crypt; if 'enhanced tab list' is disabled: limited to ~50 blocks away from the player)",
"Does " + EnumChatFormatting.ITALIC + "not" + EnumChatFormatting.RESET + " track explorer score " + EnumChatFormatting.GRAY + "(explored rooms, secrets, ...)");
Property propDungSendPerformanceOnDeath = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungSendPerformanceOnDeath", false, "Send dungeon performance after a player died?"));
Property propDungSendPerformanceOnEndScreen = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungSendPerformanceOnEndScreen", false, "Send dungeon performance on end screen?"));
Property propDungOverlayEnabled = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungOverlayEnabled", false, "Enable Dungeon performance overlay?"));
Property propDungOverlayPositionX = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungOverlayPositionX", 6, "Dungeon performance overlay position: x value", 0, 1000),
null, "‰", // per mille
(slider) -> {
MooConfig.dungOverlayPositionX = slider.getValueInt();
MooConfigGui.showDungeonPerformanceOverlayUntil = System.currentTimeMillis() + 500;
});
Property propDungOverlayPositionY = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungOverlayPositionY", 200, "Dungeon performance overlay position: y value", 0, 1000),
null, "‰", // per mille
(slider) -> {
MooConfig.dungOverlayPositionY = slider.getValueInt();
MooConfigGui.showDungeonPerformanceOverlayUntil = System.currentTimeMillis() + 500;
});
Property propDungOverlayGuiScale = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungOverlayGuiScale", 100, "Dungeon performance overlay GUI scale", 50, 200),
null, "%",
(slider) -> {
MooConfig.dungOverlayGuiScale = slider.getValueInt();
MooConfigGui.showDungeonPerformanceOverlayUntil = System.currentTimeMillis() + 500;
});
Property propDungOverlayTextBorder = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungOverlayTextBorder", "drop shadow", "Dungeon performance overlay text border", new String[]{"drop shadow", "full outline", "no border"}));
// Sub-Category: Party Finder
subCat = configCat.addSubCategory("Dungeon Party Finder");
subCat.addExplanations("Adds various indicators to the dungeon party finder",
"to make it easier to find the perfect party:",
"",
"Marks parties...",
" ‣ you cannot join, or decided to filter out: " + EnumChatFormatting.RED + "⬛",
" ‣ that do not meet all your criteria: " + EnumChatFormatting.GOLD + "⬛",
" ‣ with \"Dungeon Level Required\" below a certain level " + EnumChatFormatting.GRAY + "(if present)" + EnumChatFormatting.RESET + ": " + EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + "ᐯ" + EnumChatFormatting.RESET,
" ‣ with someone below a certain class level: " + EnumChatFormatting.RED + EnumChatFormatting.BOLD + "ᐯ" + EnumChatFormatting.RESET,
" ‣ with duplicated roles you specify: " + EnumChatFormatting.GOLD + "²⁺",
" ‣ with someone with a role you specify: " + EnumChatFormatting.GRAY + "e.g. " + EnumChatFormatting.WHITE + "H " + EnumChatFormatting.GRAY + "(class' 1ˢᵗ letter)",
" ‣ that match your criteria: " + EnumChatFormatting.GREEN + "⬛");
Property propDungPartyFinderPlayerLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungPartyFinderPlayerLookup", "as a tooltip", "Show armor + dungeons stats of player joining via party finder as a tooltip or in chat?", new String[]{"as a tooltip", "in chat", "disabled"}));
Property propDungPartyFullLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungPartyFullLookup", true, "Lookup info when party full?"));
Property propDungPartyFinderPartyLookup = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungPartyFinderPartyLookup", true, "Lookup info when joining another party?"));
Property propDungPartyFinderOverlayDrawBackground = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungPartyFinderOverlayDrawBackground", true, "Party Finder: draw colored overlay?"));
Property propDungPartiesSize = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungPartiesSize", true, "Show size of parties?"),
new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.WHITE + "1 - 4").gray()));
Property propDungDungeonReqMin = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungDungeonReqMin", 0, "Marks parties with lower Dungeon level req than this value")
.setMinValue(0).setMaxValue(50),
new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.DARK_RED + EnumChatFormatting.BOLD + "ᐯ").gray()));
Property propDungClassMin = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungClassMin", 0, "Marks parties with members with lower class level than this value")
.setMinValue(0).setMaxValue(50),
new MooConfigPreview(new MooChatComponent("Marked with: " + EnumChatFormatting.RED + EnumChatFormatting.BOLD + "ᐯ").gray()));
Property propDungMarkPartiesWithArcher = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungMarkPartiesWithArcher", "if dupe: " + EnumChatFormatting.GOLD + "⬛", "Mark parties with Archer class?",
new String[]{
"if dupe: " + EnumChatFormatting.GOLD + "⬛", "always: " + EnumChatFormatting.GOLD + "⬛",
"if dupe: " + EnumChatFormatting.RED + "⬛", "always: " + EnumChatFormatting.RED + "⬛",
"do not mark"}),
new MooConfigPreview(DataHelper.DungeonClass.ARCHER));
Property propDungMarkPartiesWithBerserk = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungMarkPartiesWithBerserk", "do not mark", "Mark parties with Berserk class?",
new String[]{
"if dupe: " + EnumChatFormatting.GOLD + "⬛", "always: " + EnumChatFormatting.GOLD + "⬛",
"if dupe: " + EnumChatFormatting.RED + "⬛", "always: " + EnumChatFormatting.RED + "⬛",
"do not mark"}),
new MooConfigPreview(DataHelper.DungeonClass.BERSERK));
Property propDungMarkPartiesWithHealer = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungMarkPartiesWithHealer", "do not mark", "Mark parties with Healer class?",
new String[]{
"if dupe: " + EnumChatFormatting.GOLD + "⬛", "always: " + EnumChatFormatting.GOLD + "⬛",
"if dupe: " + EnumChatFormatting.RED + "⬛", "always: " + EnumChatFormatting.RED + "⬛",
"do not mark"}),
new MooConfigPreview(DataHelper.DungeonClass.HEALER));
Property propDungMarkPartiesWithMage = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungMarkPartiesWithMage", "do not mark", "Mark parties with Mage class?",
new String[]{
"if dupe: " + EnumChatFormatting.GOLD + "⬛", "always: " + EnumChatFormatting.GOLD + "⬛",
"if dupe: " + EnumChatFormatting.RED + "⬛", "always: " + EnumChatFormatting.RED + "⬛",
"do not mark"}),
new MooConfigPreview(DataHelper.DungeonClass.MAGE));
Property propDungMarkPartiesWithTank = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungMarkPartiesWithTank", "do not mark", "Mark parties with Tank class?",
new String[]{
"if dupe: " + EnumChatFormatting.GOLD + "⬛", "always: " + EnumChatFormatting.GOLD + "⬛",
"if dupe: " + EnumChatFormatting.RED + "⬛", "always: " + EnumChatFormatting.RED + "⬛",
"do not mark"}),
new MooConfigPreview(DataHelper.DungeonClass.TANK));
Property propDungPartyFinderRuleEditorSimplified = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungPartyFinderRuleEditorSimplified", true, "Use simplified version of rules editor?"));
Property propDungPartyFinderRuleEditorShowOpenButton = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungPartyFinderRuleEditorShowOpenButton", true, "Show button to open rules editor?"));
Property propDungSendWrongFloorWarning = subCat.addConfigEntry(cfg.get(configCat.getConfigName(),
"dungSendWrongFloorWarning", true, "Send warning when entering wrong floor?"));
boolean modifiedMooCmdAlias = false;
String mooCmdAliasPreChange = mooCmdAlias;
boolean modifiedTabCompletableCommandsList = false;
String[] tabCompletableCommandsPreChange = tabCompletableNamesCommands != null ? tabCompletableNamesCommands.clone() : null;
boolean modifiedTooltipAuctionHousePriceEachEnchantments = false;
String[] tooltipAuctionHousePriceEachEnchantmentsPreChange = tooltipAuctionHousePriceEachEnchantments != null ? tooltipAuctionHousePriceEachEnchantments.clone() : null;
if (readFieldsFromConfig) {
// Category: General
configGuiExplanations = propConfigGuiExplanations.getString();
hasOpenedConfigGui = propHasOpenedConfigGui.getBoolean();
mooCmdAlias = propMooCmdAlias.getString();
fixReplyCmd = propFixReplyCmd.getBoolean();
enableCopyInventory = propEnableCopyInventory.getBoolean();
wailaLevelOfDetail = propWailaLevelOfDetail.getString();
copyOrSaveWailaAndInventoryData = propCopyOrSaveWailaAndInventoryData.getString();
tabCompletableNamesCommands = propTabCompletableNamesCommands.getStringList();
logsDirs = propLogsDirs.getStringList();
defaultStartDate = propDefaultStartDate.getString().trim();
maxLogFileSize = propMaxLogFileSize.getInt();
maxLatestLogFileSize = propMaxLatestLogFileSize.getInt();
// Category: Notifications
doUpdateCheck = propDoUpdateCheck.getBoolean();
showBestFriendNotifications = propShowBestFriendNotifications.getBoolean();
enableBestFriendNotificationSound = propEnableBestFriendNotificationSound.getBoolean();
showFriendNotifications = propShowFriendNotifications.getBoolean();
showGuildNotifications = propShowGuildNotifications.getBoolean();
doBestFriendsOnlineCheck = propDoBestFriendsOnlineCheck.getBoolean();
// Category: SkyBlock
enableSkyBlockOnlyFeatures = propEnableSkyBlockOnlyFeatures.getString();
notifyFreshServer = propNotifyFreshServer.getInt();
notifyOldServer = propNotifyOldServer.getInt();
notifyServerAge = propNotifyServerAge.getBoolean();
chestAnalyzerShowBazaarItems = propChestAnalyzerShowBazaarItems.getBoolean();
chestAnalyzerUseBazaarPrices = propChestAnalyzerUseBazaarPrices.getString();
chestAnalyzerShowLowestBinItems = propChestAnalyzerShowLowestBinItems.getBoolean();
chestAnalyzerShowNpcItems = propChestAnalyzerShowNpcItems.getBoolean();
chestAnalyzerShowNoPriceItems = propChestAnalyzerShowNoPriceItems.getBoolean();
chestAnalyzerShowCommandUsage = propChestAnalyzerShowCommandUsage.getBoolean();
tooltipToggleKeyBinding = propTooltipToggleKeyBinding.getInt();
tooltipItemAge = propTooltipItemAge.getString();
tooltipItemAgeShortened = propTooltipItemAgeShortened.getBoolean();
tooltipItemTimestamp = propTooltipItemTimestamp.getString();
showPetExp = propShowPetExp.getString();
numeralSystem = propNumeralSystem.getString();
tooltipAuctionHousePriceEach = propTooltipAuctionHousePriceEach.getString();
tooltipAuctionHousePriceEachEnchantments = propTooltipAuctionHousePriceEachEnchantments.getStringList();
auctionHouseMarkEndedAuctions = propAuctionHouseMarkEndedAuctions.getString();
bazaarSellAllOrder = propBazaarSellAllOrder.getString();
bazaarSellAllOrderAscDesc = propBazaarSellAllOrderAscDesc.getString();
bazaarShowItemsLeft = propBazaarShowItemsLeft.getBoolean();
bazaarConnectGraphsNodes = propBazaarConnectGraphsNodes.getString();
bazaarConnectGraphsLineWidth = propBazaarConnectGraphsLineWidth.getInt();
bestiaryOverviewOrder = propBestiaryOverviewOrder.getString();
lookupWikiKeyBinding = propLookupWikiKeyBinding.getInt();
lookupWikiPreferredWiki = propLookupWikiPreferredWiki.getString();
lookupPriceKeyBinding = propLookupPriceKeyBinding.getInt();
lookupItemDirectly = propLookupItemDirectly.getBoolean();
// Category: SkyBlock Dungeons
showItemQualityAndFloor = propShowItemQualityAndFloor.getString();
dungItemQualityPos = propDungItemQualityPos.getString();
dungItemQualityShortenNonRandomized = propDungItemQualityShortenNonRandomized.getBoolean();
dungItemHideGearScore = propDungItemHideGearScore.getBoolean();
dungItemToolTipToggleKeyBinding = propDungItemToolTipToggleKeyBinding.getInt();
dungSendPerformanceOnDeath = propDungSendPerformanceOnDeath.getBoolean();
dungSendPerformanceOnEndScreen = propDungSendPerformanceOnEndScreen.getBoolean();
dungOverlayEnabled = propDungOverlayEnabled.getBoolean();
dungOverlayPositionX = propDungOverlayPositionX.getInt();
dungOverlayPositionY = propDungOverlayPositionY.getInt();
dungOverlayGuiScale = propDungOverlayGuiScale.getInt();
dungOverlayTextBorder = propDungOverlayTextBorder.getString();
dungPartyFinderPlayerLookup = propDungPartyFinderPlayerLookup.getString();
dungPartyFullLookup = propDungPartyFullLookup.getBoolean();
dungPartyFinderPartyLookup = propDungPartyFinderPartyLookup.getBoolean();
dungPartyFinderOverlayDrawBackground = propDungPartyFinderOverlayDrawBackground.getBoolean();
dungPartiesSize = propDungPartiesSize.getBoolean();
dungDungeonReqMin = propDungDungeonReqMin.getInt();
dungClassMin = propDungClassMin.getInt();
dungMarkPartiesWithArcher = propDungMarkPartiesWithArcher.getString();
dungMarkPartiesWithBerserk = propDungMarkPartiesWithBerserk.getString();
dungMarkPartiesWithHealer = propDungMarkPartiesWithHealer.getString();
dungMarkPartiesWithMage = propDungMarkPartiesWithMage.getString();
dungMarkPartiesWithTank = propDungMarkPartiesWithTank.getString();
dungPartyFinderRuleEditorSimplified = propDungPartyFinderRuleEditorSimplified.getBoolean();
dungPartyFinderRuleEditorShowOpenButton = propDungPartyFinderRuleEditorShowOpenButton.getBoolean();
dungSendWrongFloorWarning = propDungSendWrongFloorWarning.getBoolean();
if (!hasOpenedConfigGui && (!propDungOverlayEnabled.isDefault() || !propDungOverlayPositionX.isDefault() || !propDungOverlayPositionY.isDefault() || !propDungOverlayGuiScale.isDefault())) {
// player hasn't opened config gui yet and but already moved the dungeon overlay
hasOpenedConfigGui = true;
}
if (!StringUtils.equals(mooCmdAliasPreChange, mooCmdAlias)) {
modifiedMooCmdAlias = true;
}
if (!Arrays.equals(tabCompletableCommandsPreChange, tabCompletableNamesCommands)) {
modifiedTabCompletableCommandsList = true;
}
if (!Arrays.equals(tooltipAuctionHousePriceEachEnchantmentsPreChange, tooltipAuctionHousePriceEachEnchantments)) {
modifiedTooltipAuctionHousePriceEachEnchantments = true;
}
}
// Category: General
propConfigGuiExplanations.set(configGuiExplanations);
propHasOpenedConfigGui.set(hasOpenedConfigGui);
propMooCmdAlias.set(mooCmdAlias);
propFixReplyCmd.set(fixReplyCmd);
propEnableCopyInventory.set(enableCopyInventory);
propWailaLevelOfDetail.set(wailaLevelOfDetail);
propCopyOrSaveWailaAndInventoryData.set(copyOrSaveWailaAndInventoryData);
propTabCompletableNamesCommands.set(tabCompletableNamesCommands);
propLogsDirs.set(logsDirs);
propDefaultStartDate.set(defaultStartDate);
propMaxLogFileSize.set(maxLogFileSize);
propMaxLatestLogFileSize.set(maxLatestLogFileSize);
// Category: Notifications
propDoUpdateCheck.set(doUpdateCheck);
propShowBestFriendNotifications.set(showBestFriendNotifications);
propEnableBestFriendNotificationSound.set(enableBestFriendNotificationSound);
propShowFriendNotifications.set(showFriendNotifications);
propShowGuildNotifications.set(showGuildNotifications);
propDoBestFriendsOnlineCheck.set(doBestFriendsOnlineCheck);
// Category: SkyBlock
propEnableSkyBlockOnlyFeatures.set(enableSkyBlockOnlyFeatures);
propNotifyFreshServer.set(notifyFreshServer);
propNotifyOldServer.set(notifyOldServer);
propNotifyServerAge.set(notifyServerAge);
propChestAnalyzerShowBazaarItems.set(chestAnalyzerShowBazaarItems);
propChestAnalyzerUseBazaarPrices.set(chestAnalyzerUseBazaarPrices);
propChestAnalyzerShowLowestBinItems.set(chestAnalyzerShowLowestBinItems);
propChestAnalyzerShowNpcItems.set(chestAnalyzerShowNpcItems);
propChestAnalyzerShowNoPriceItems.set(chestAnalyzerShowNoPriceItems);
propChestAnalyzerShowCommandUsage.set(chestAnalyzerShowCommandUsage);
propTooltipToggleKeyBinding.set(tooltipToggleKeyBinding);
propTooltipItemAge.set(tooltipItemAge);
propTooltipItemAgeShortened.set(tooltipItemAgeShortened);
propTooltipItemTimestamp.set(tooltipItemTimestamp);
propShowPetExp.set(showPetExp);
propNumeralSystem.set(numeralSystem);
propTooltipAuctionHousePriceEach.set(tooltipAuctionHousePriceEach);
propTooltipAuctionHousePriceEachEnchantments.set(tooltipAuctionHousePriceEachEnchantments);
propAuctionHouseMarkEndedAuctions.set(auctionHouseMarkEndedAuctions);
propBazaarSellAllOrder.set(bazaarSellAllOrder);
propBazaarSellAllOrderAscDesc.set(bazaarSellAllOrderAscDesc);
propBazaarShowItemsLeft.set(bazaarShowItemsLeft);
propBazaarConnectGraphsNodes.set(bazaarConnectGraphsNodes);
propBazaarConnectGraphsLineWidth.set(bazaarConnectGraphsLineWidth);
propBestiaryOverviewOrder.set(bestiaryOverviewOrder);
propLookupWikiKeyBinding.set(lookupWikiKeyBinding);
propLookupWikiPreferredWiki.set(lookupWikiPreferredWiki);
propLookupPriceKeyBinding.set(lookupPriceKeyBinding);
propLookupItemDirectly.set(lookupItemDirectly);
// Category: SkyBlock Dungeons
propShowItemQualityAndFloor.set(showItemQualityAndFloor);
propDungItemQualityPos.set(dungItemQualityPos);
propDungItemQualityShortenNonRandomized.set(dungItemQualityShortenNonRandomized);
propDungItemHideGearScore.set(dungItemHideGearScore);
propDungItemToolTipToggleKeyBinding.set(dungItemToolTipToggleKeyBinding);
propDungSendPerformanceOnDeath.set(dungSendPerformanceOnDeath);
propDungSendPerformanceOnEndScreen.set(dungSendPerformanceOnEndScreen);
propDungOverlayEnabled.set(dungOverlayEnabled);
propDungOverlayPositionX.set(dungOverlayPositionX);
propDungOverlayPositionY.set(dungOverlayPositionY);
propDungOverlayGuiScale.set(dungOverlayGuiScale);
propDungOverlayTextBorder.set(dungOverlayTextBorder);
propDungPartyFinderPlayerLookup.set(dungPartyFinderPlayerLookup);
propDungPartyFullLookup.set(dungPartyFullLookup);
propDungPartyFinderPartyLookup.set(dungPartyFinderPartyLookup);
propDungPartyFinderOverlayDrawBackground.set(dungPartyFinderOverlayDrawBackground);
propDungPartiesSize.set(dungPartiesSize);
propDungDungeonReqMin.set(dungDungeonReqMin);
propDungClassMin.set(dungClassMin);
propDungMarkPartiesWithArcher.set(dungMarkPartiesWithArcher);
propDungMarkPartiesWithBerserk.set(dungMarkPartiesWithBerserk);
propDungMarkPartiesWithHealer.set(dungMarkPartiesWithHealer);
propDungMarkPartiesWithMage.set(dungMarkPartiesWithMage);
propDungMarkPartiesWithTank.set(dungMarkPartiesWithTank);
propDungPartyFinderRuleEditorSimplified.set(dungPartyFinderRuleEditorSimplified);
propDungPartyFinderRuleEditorShowOpenButton.set(dungPartyFinderRuleEditorShowOpenButton);
propDungSendWrongFloorWarning.set(dungSendWrongFloorWarning);
if (saveToFile && cfg.hasChanged()) {
boolean isPlayerIngame = Minecraft.getMinecraft().thePlayer != null;
if (modifiedMooCmdAlias) {
Map clientCommandsMap = ClientCommandHandler.instance.getCommands();
ICommand possibleClientCommand = clientCommandsMap.get(mooCmdAlias);
if (possibleClientCommand != null && !(possibleClientCommand instanceof MooCommand)) {
// tried to use a command name which is already used by another client side command; however, this would overwrite the original command
if (isPlayerIngame) {
main.getChatHelper().sendMessage(EnumChatFormatting.GOLD, " ⚠ " + EnumChatFormatting.GOLD + "Client-side commands from other mods cannot be used as a command alias. " + EnumChatFormatting.RED + "This would overwrite the other command! Therefore the alias for " + EnumChatFormatting.DARK_RED + "/moo" + EnumChatFormatting.RED + " was not changed to " + EnumChatFormatting.DARK_RED + "/" + mooCmdAlias);
}
mooCmdAlias = mooCmdAliasPreChange;
propMooCmdAlias.set(mooCmdAlias);
} else if (isPlayerIngame) {
if (StringUtils.isEmpty(mooCmdAlias)) {
main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Removed command alias for " + EnumChatFormatting.DARK_RED + "/moo " + EnumChatFormatting.RED + "which takes effect after a game restart.");
} else {
main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Changed command alias for " + EnumChatFormatting.DARK_RED + "/moo " + EnumChatFormatting.RED + "to " + EnumChatFormatting.DARK_RED + "/" + mooCmdAlias + EnumChatFormatting.RED + " which takes effect after a game restart.");
}
}
}
if (modifiedTabCompletableCommandsList) {
if (isPlayerIngame) {
main.getChatHelper().sendMessage(EnumChatFormatting.RED, "Added or removed commands with tab-completable usernames take effect after a game restart! If player names cannot be tab-completed for a command after a game restart, check the capitalization of the command name.");
}
Map clientCommandsMap = ClientCommandHandler.instance.getCommands();
List removedCommands = new ArrayList<>();
for (String tabCompletableCommandName : tabCompletableNamesCommands) {
ICommand possibleClientCommand = clientCommandsMap.get(tabCompletableCommandName);
if (possibleClientCommand != null && !(possibleClientCommand instanceof TabCompletableCommand)) {
// tried to add a client side command to tab-completable commands; however, this would overwrite the original command
removedCommands.add(tabCompletableCommandName);
}
}
if (removedCommands.size() > 0) {
if (isPlayerIngame) {
main.getChatHelper().sendMessage(EnumChatFormatting.GOLD, " ⚠ " + EnumChatFormatting.GOLD + "Client-side commands from other mods cannot be added to commands with tab-completable usernames. " + EnumChatFormatting.RED + "This would overwrite the other command! Therefore the following commands have been removed from the list of commands with tab-completable usernames: " + EnumChatFormatting.GOLD + String.join(EnumChatFormatting.RED + ", " + EnumChatFormatting.GOLD, removedCommands));
}
tabCompletableNamesCommands = (String[]) ArrayUtils.removeElements(tabCompletableNamesCommands, removedCommands.toArray());
propTabCompletableNamesCommands.set(tabCompletableNamesCommands);
}
}
if (modifiedTooltipAuctionHousePriceEachEnchantments) {
for (int i = 0, enchantmentsLength = tooltipAuctionHousePriceEachEnchantments.length; i < enchantmentsLength; i++) {
// standardize enchantment names to match their names in the NBT data
String enchantmentName = tooltipAuctionHousePriceEachEnchantments[i];
String standardizedEnchantmentName = enchantmentName.toLowerCase().replace(' ', '_');
tooltipAuctionHousePriceEachEnchantments[i] = standardizedEnchantmentName;
}
propTooltipAuctionHousePriceEachEnchantments.set(tooltipAuctionHousePriceEachEnchantments);
}
cfg.save();
}
}
/**
* Tries to find/resolve default directories containing minecraft logfiles (in .log.gz format)
*
* @return list of /logs/ directories
*/
private String[] resolveDefaultLogsDirs() {
List logsDirs = new ArrayList<>();
File currentMcLogsDirFile = new File(Minecraft.getMinecraft().mcDataDir, "logs");
if (currentMcLogsDirFile.exists() && currentMcLogsDirFile.isDirectory()) {
String currentMcLogsDir = Utils.toRealPath(currentMcLogsDirFile);
logsDirs.add(currentMcLogsDir);
}
String defaultMcLogsDir = System.getProperty("user.home");
Util.EnumOS osType = Util.getOSType();
// default directories for .minecraft: https://minecraft.gamepedia.com/.minecraft
switch (osType) {
case WINDOWS:
defaultMcLogsDir += "\\AppData\\Roaming\\.minecraft\\logs";
break;
case OSX:
defaultMcLogsDir += "/Library/Application Support/minecraft/logs";
break;
default:
defaultMcLogsDir += "/.minecraft/logs";
}
File defaultMcLogsDirFile = new File(defaultMcLogsDir);
if (defaultMcLogsDirFile.exists() && defaultMcLogsDirFile.isDirectory() && !currentMcLogsDirFile.equals(defaultMcLogsDirFile)) {
logsDirs.add(Utils.toRealPath(defaultMcLogsDirFile));
}
return logsDirs.toArray(new String[]{});
}
/**
* @return max log file size in Bytes
*/
public static long getMaxLogFileSize() {
return maxLogFileSize * 1024L;
}
/**
* @return max latest.log file size in Bytes
*/
public static long getMaxLatestLogFileSize() {
return maxLatestLogFileSize * 1024L;
}
// Category: General
public static Setting getConfigGuiExplanationsDisplay() {
return Setting.get(configGuiExplanations);
}
public void theyOpenedTheConfigGui() {
if (!hasOpenedConfigGui) {
// opened config gui for the first time!
main.getChatHelper().sendMessage(new MooChatComponent(EnumChatFormatting.DARK_GREEN + " ❢ " + EnumChatFormatting.GREEN + "To configure " + EnumChatFormatting.GOLD + "Cowlection " + EnumChatFormatting.GREEN + "features use " + EnumChatFormatting.LIGHT_PURPLE + "/moo config").green()
.setSuggestCommand("/moo config")
.appendFreshSibling(new MooChatComponent(" (this message will be displayed only once)").gray()));
hasOpenedConfigGui = true;
syncFromFields();
}
}
public static boolean keepFullWailaInfo() {
return "all info".equals(wailaLevelOfDetail);
}
public static boolean copyWailaAndInventoryDataToClipboard() {
return "to clipboard".equals(copyOrSaveWailaAndInventoryData);
}
// Category: Notifications
/**
* Should login/logout notifications be modified and thus monitored?
*
* @return true if notifications should be monitored
*/
public static boolean doMonitorNotifications() {
return showBestFriendNotifications || enableBestFriendNotificationSound || !showFriendNotifications || !showGuildNotifications;
}
// Category: SkyBlock
public static Setting getEnableSkyBlockOnlyFeatures() {
return Setting.get(enableSkyBlockOnlyFeatures);
}
public static boolean useInstantSellBazaarPrices() {
return "Instant-Sell".equals(chestAnalyzerUseBazaarPrices);
}
public static Setting getTooltipAuctionHousePriceEachDisplay() {
return Setting.get(tooltipAuctionHousePriceEach);
}
public static Setting getMarkAuctionHouseEndedAuctions() {
return Setting.get(auctionHouseMarkEndedAuctions);
}
public static Setting getBazaarConnectGraphsNodes() {
return Setting.get(bazaarConnectGraphsNodes);
}
public static Setting getTooltipItemAgeDisplay() {
return Setting.get(tooltipItemAge);
}
public static Setting getTooltipItemTimestampDisplay() {
return Setting.get(tooltipItemTimestamp);
}
public static Setting getTooltipPetExpDisplay() {
return Setting.get(showPetExp);
}
public static boolean useRomanNumerals() {
return numeralSystem.startsWith("Roman");
}
public void cycleBestiaryOverviewOrder() {
String oldValue = bestiaryOverviewOrder;
int currentIndex = -1;
for (int i = 0; i < bestiaryOverviewOrderDefaultValues.length; i++) {
if (oldValue.equals(bestiaryOverviewOrderDefaultValues[i])) {
currentIndex = i;
break;
}
}
bestiaryOverviewOrder = bestiaryOverviewOrderDefaultValues[(currentIndex + 1) % bestiaryOverviewOrderDefaultValues.length];
syncFromFields();
}
public static boolean isTooltipToggleKeyBindingPressed() {
return tooltipToggleKeyBinding > 0 && Keyboard.isKeyDown(tooltipToggleKeyBinding);
}
/**
* @return true if unofficial wiki is preferred but shift isn't pressed, or if unofficial wiki isn't preferred and shift is pressed
*/
public static boolean isLookupWikiKeyBindingPressed() {
return isLookupAnyWikiKeyBindingPressed() && ("unofficial".equals(lookupWikiPreferredWiki) ^ GuiScreen.isShiftKeyDown());
}
/**
* @return true if official wiki is preferred but shift isn't pressed, or if official wiki isn't preferred and shift is pressed
*/
public static boolean isLookupOfficialWikiKeyBindingPressed() {
return isLookupAnyWikiKeyBindingPressed() && ("official".equals(lookupWikiPreferredWiki) ^ GuiScreen.isShiftKeyDown());
}
private static boolean isLookupAnyWikiKeyBindingPressed() {
return lookupWikiKeyBinding > 0 && Keyboard.isKeyDown(lookupWikiKeyBinding);
}
public static boolean isLookupPriceKeyBindingPressed() {
return lookupPriceKeyBinding > 0 && Keyboard.isKeyDown(lookupPriceKeyBinding);
}
// Category: SkyBlock Dungeons
public static Setting getShowItemQualityAndFloorDisplay() {
return Setting.get(showItemQualityAndFloor);
}
public static boolean isDungItemQualityAtTop() {
return dungItemQualityPos.equals("top");
}
public static boolean isDungeonItemTooltipToggleKeyBindingPressed() {
return dungItemToolTipToggleKeyBinding > 0 && Keyboard.isKeyDown(dungItemToolTipToggleKeyBinding);
}
public static Setting getDungOverlayTextBorder() {
switch (dungOverlayTextBorder) {
case "drop shadow":
return Setting.TEXT;
case "full outline":
return Setting.ALWAYS;
default:
// everything else: "no border"
return Setting.DISABLED;
}
}
public static Setting getDungPartyFinderPlayerLookupDisplay() {
return Setting.get(dungPartyFinderPlayerLookup);
}
public static Mark dungeonPartyMarker(DataHelper.DungeonClass dungeonClass) {
String setting;
switch (dungeonClass) {
case ARCHER:
setting = dungMarkPartiesWithArcher;
break;
case BERSERK:
setting = dungMarkPartiesWithBerserk;
break;
case HEALER:
setting = dungMarkPartiesWithHealer;
break;
case MAGE:
setting = dungMarkPartiesWithMage;
break;
case TANK:
setting = dungMarkPartiesWithTank;
break;
default:
setting = "do not mark";
break;
}
switch (setting) {
case "if dupe: §6⬛":
return Mark.UNIDEAL_DUPE;
case "always: §6⬛":
return Mark.UNIDEAL_ALWAYS;
case "if dupe: §c⬛":
return Mark.BLOCK_DUPE;
case "always: §c⬛":
return Mark.BLOCK_ALWAYS;
default: // do not mark
return Mark.DO_NOT_MARK;
}
}
// MC Log Search:
public static LocalDate calculateStartDate() {
try {
// date format: yyyy-mm-dd
return LocalDate.parse(defaultStartDate);
} catch (DateTimeParseException e) {
// fall-through
}
try {
int months = Integer.parseInt(defaultStartDate);
return LocalDate.now().minus(months, ChronoUnit.MONTHS);
} catch (NumberFormatException e) {
// default: 1 month
return LocalDate.now().minus(1, ChronoUnit.MONTHS);
}
}
// other stuff
public static List getConfigCategories() {
return configCategories;
}
public Property getMooCmdAliasProperty() {
return propMooCmdAlias;
}
public Property getTabCompletableNamesCommandsProperty() {
return propTabCompletableNamesCommands;
}
public List getLogSearchProperties() {
return logSearchProperties;
}
public Property getTooltipAuctionHousePriceEachEnchantmentsProperty() {
return propTooltipAuctionHousePriceEachEnchantments;
}
public class ConfigEventHandler {
@SubscribeEvent(priority = EventPriority.NORMAL)
public void onEvent(ConfigChangedEvent.OnConfigChangedEvent e) {
if (Cowlection.MODID.equals(e.modID)) {
syncFromGui();
}
}
}
public enum Mark {
UNIDEAL_ALWAYS, UNIDEAL_DUPE, //
BLOCK_ALWAYS, BLOCK_DUPE, //
DO_NOT_MARK;
public boolean ifDuped() {
return this == UNIDEAL_DUPE || this == BLOCK_DUPE;
}
}
public enum Setting {
UNKNOWN, DISABLED, //
ALWAYS, TOOLTIP, TEXT, //
SPECIAL;
public static Setting get(String configValue) {
switch (configValue) {
case "always":
return ALWAYS;
case "as a tooltip":
case "as tooltip":
return TOOLTIP;
case "in chat":
case "as text":
case "a word":
return TEXT;
case "hidden":
case "never":
case "disabled":
return DISABLED;
case "on SkyBlock":
case "key press":
case "a letter":
return SPECIAL;
default:
return UNKNOWN;
}
}
}
}