From fcf1a7b2b39b03b726e4c356bc265b7341217602 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 14 Jun 2020 21:54:45 +1000 Subject: BETA-1.6 but for real this time --- .../moulberry/notenoughupdates/CustomItems.java | 64 ++ .../moulberry/notenoughupdates/GuiItemUsages.java | 2 + .../github/moulberry/notenoughupdates/NEUIO.java | 49 +- .../moulberry/notenoughupdates/NEUManager.java | 525 ++++++++------ .../moulberry/notenoughupdates/NEUOverlay.java | 799 ++++++++++++--------- .../notenoughupdates/NotEnoughUpdates.java | 106 ++- .../notenoughupdates/infopanes/DevInfoPane.java | 105 ++- .../infopanes/FlipperInfoPane.java | 20 +- .../notenoughupdates/infopanes/HTMLInfoPane.java | 45 +- .../notenoughupdates/infopanes/InfoPane.java | 14 +- .../notenoughupdates/infopanes/QOLInfoPane.java | 33 +- .../infopanes/SettingsInfoPane.java | 25 +- .../notenoughupdates/infopanes/TextInfoPane.java | 24 +- .../mixins/MixinInventoryEffectRenderer.java | 24 + .../notenoughupdates/options/Options.java | 33 +- .../notenoughupdates/util/HypixelApi.java | 5 +- .../moulberry/notenoughupdates/util/TexLoc.java | 3 + .../assets/notenoughupdates/rightarrow_overlay.png | Bin 1112 -> 2715 bytes src/main/resources/mixins.notenoughupdates.json | 3 +- 19 files changed, 1251 insertions(+), 628 deletions(-) diff --git a/src/main/java/io/github/moulberry/notenoughupdates/CustomItems.java b/src/main/java/io/github/moulberry/notenoughupdates/CustomItems.java index c4d3b57b..148f1bc5 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/CustomItems.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/CustomItems.java @@ -1,4 +1,68 @@ package io.github.moulberry.notenoughupdates; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import net.minecraft.util.EnumChatFormatting; + public class CustomItems { + + /** + * So it has come to this, huh? Snooping through the source to find all my carefully crafted easter eggs. Well + * guess what, you cheated not only the game, but yourself. You didn't grow. You didn't improve. You took a + * shortcut and gained nothing. You experienced a hollow victory. Nothing was risked and nothing was gained. + * It's sad that you don't know the difference. + */ + + public static JsonObject NULLZEE = create( + "NULLZEE", + "dirt", + "Nullzee242 Youtube Channel", + "Dirt, AOTD. Dirt, AOTD.", + "Dirt, AOTD. Dirt, AOTD.", + "Ooh, Dirt to Midas! Let's shake it up a little.", + "", + "Also, Did you know that only 10% of the people watching are subscribed?", + "It's OK, everyone makes mistakes", + "Also follow -> twitch.tv/nullzeelive", + "Also -> discord.gg/nullzee"); + public static JsonObject DUCTTAPE = create( + "DUCTTAPE", + "iron_shovel", + "You ever accidentally bury your duct tape?", + "No problem! Our team of experts specialise in", + "subterranean duct tape excavation. That's right:", + "your buried duct tape problems are a thing of the past,", + "all for the low price of $7.99 or a subscription", + "to the Ducttapedigger youtube channel!"); + public static JsonObject RUNE = create("RUNE", "paper", "No.", "I hate runes."); + public static JsonObject TWOBEETWOTEE = create("2B2T", "bedrock", "Minecraft's oldest anarchy Minecraft server in Minecraft.", + "This Minecraft anarchy server is the oldest server,", + "being a server since 2010 when Minecraft was a game with a server.", + "It is complete anarchy in Minecraft which means that there is total anarchy.", + "Hacking is allowed in Minecraft on this anarchy server which", + "is the oldest anarchy server in Minecraft, 2b2t. Hack. Steal. Cheat. Lie.", + "On the oldest anarchy server in Minecraft. 2b2t. The worst server in Minecraft,", + "where there are no rules. On the oldest anarchy server in Minecraft.", + "In this Minecraft anarchy server, there have been numerous Minecraft", + "incursions on the server, some of which I, a player on this Minecraft", + "anarchy server in Minecraft, have participated in. One of this server's", + "most infamous Minecraft players on the oldest Minecraft"); + + /** + * SHAAAAAAAAAAAAAAAAAAME + */ + + private static JsonObject create(String internalname, String itemid, String displayname, String... lore) { + JsonObject json = new JsonObject(); + json.addProperty("itemid", itemid); + json.addProperty("internalname", internalname); + json.addProperty("displayname", EnumChatFormatting.RED+displayname); + JsonArray jsonlore = new JsonArray(); + for(String line : lore) { + jsonlore.add(new JsonPrimitive(EnumChatFormatting.GRAY + line)); + } + json.add("lore", jsonlore); + return json; + } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/GuiItemUsages.java b/src/main/java/io/github/moulberry/notenoughupdates/GuiItemUsages.java index ceb4e5d4..49a1be51 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/GuiItemUsages.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/GuiItemUsages.java @@ -55,6 +55,8 @@ public class GuiItemUsages extends GuiCrafting { } if(results.get(currentIndex).has("crafttext")) { craftText = results.get(currentIndex).get("crafttext").getAsString(); + } else { + craftText = ""; } cw.craftResult.setInventorySlotContents(0, manager.jsonToStack(results.get(currentIndex))); } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUIO.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUIO.java index a89bd1dc..a7c7a1e4 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUIO.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUIO.java @@ -3,15 +3,20 @@ package io.github.moulberry.notenoughupdates; import org.kohsuke.github.*; import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; public class NEUIO { private final String accessToken; + /** + * THIS CLASS PROVIDES METHODS FOR INTERFACING WITH THE GIT REPOSITORY NotEnoughUpdates-REPO. THIS REPOSITORY + * CONTAINS ALL THE JSON ITEMS. THIS SHOULD NOT BE A PERMANENT SOLUTION AND I SHOULD LOOK AT USING SOME FORM OF + * HOSTING SERVICE OTHER THAN A GIT REPOSITORY IF THE USERBASE OF THE MOD GROWS SIGNIFICANTLY. UNFORTUNATELY I + * CANT AFFORD HOSTING RIGHT NOW SO THIS IS WHAT YOU GET AND GITHUB WILL PROBABLY THROW A FIT IF A LARGE NUMBER + * OF USERS START DOWNLOADING FROM THE REPO ALL AT ONCE. + */ + public NEUIO(String accessToken) { this.accessToken = accessToken; } @@ -68,18 +73,50 @@ public class NEUIO { GitHub github = new GitHubBuilder().withOAuthToken(accessToken).build(); GHRepository repo = github.getRepositoryById("247692460"); - for(GHContent content : repo.getDirectoryContent("items")) { + for(GHTreeEntry treeEntry : repo.getTreeRecursive("master", 1).getTree()) { + if(treeEntry.getPath().startsWith("items/")) { + String[] split = treeEntry.getPath().split("/"); + String name = split[split.length-1]; + + String oldSha = oldShas.get(name); + if(!treeEntry.getSha().equals(oldSha)) { + changedFiles.put(name, treeEntry.getSha()); + } + } + } + + /*for(GHContent content : repo.getDirectoryContent("items")) { String oldSha = oldShas.get(content.getName()); if(!content.getSha().equals(oldSha)) { changedFiles.put(content.getName(), content.getSha()); } - } + }*/ } catch(IOException e) { return null; } return changedFiles; } + public Set getRemovedItems(Set currentlyInstalled) { + Set removedItems = new HashSet<>(); + Set repoItems = new HashSet<>(); + try { + GitHub github = new GitHubBuilder().withOAuthToken(accessToken).build(); + GHRepository repo = github.getRepositoryById("247692460"); + + for(GHTreeEntry treeEntry : repo.getTreeRecursive("master", 1).getTree()) { + String[] split = treeEntry.getPath().split("/"); + repoItems.add(split[split.length-1].split("\\.")[0]); + } + } catch(IOException e) { + e.printStackTrace(); + return removedItems; + } + removedItems.addAll(currentlyInstalled); + removedItems.removeAll(repoItems); + return removedItems; + } + /** * Takes set of filename (eg. BOW.json) and returns map from that filename to the individual download link. */ diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java index 08939cca..bebd090c 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java @@ -6,15 +6,18 @@ import io.github.moulberry.notenoughupdates.options.Options; import io.github.moulberry.notenoughupdates.util.HypixelApi; import javafx.scene.control.Alert; import net.minecraft.client.Minecraft; +import net.minecraft.client.settings.KeyBinding; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.*; +import net.minecraft.network.play.client.C0DPacketCloseWindow; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.ResourceLocation; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; import org.apache.commons.lang3.tuple.Pair; +import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.Display; import javax.swing.*; @@ -40,6 +43,14 @@ public class NEUManager { private TreeMap>> titleWordMap = new TreeMap<>(); private TreeMap>> loreWordMap = new TreeMap<>(); + public final KeyBinding keybindGive = new KeyBinding("Add item to inventory (Creative-only)", Keyboard.KEY_L, "NotEnoughUpdates"); + public final KeyBinding keybindFavourite = new KeyBinding("Set item as favourite", Keyboard.KEY_F, "NotEnoughUpdates"); + public final KeyBinding keybindViewUsages = new KeyBinding("Show usages for item", Keyboard.KEY_U, "NotEnoughUpdates"); + public final KeyBinding keybindViewRecipe = new KeyBinding("Show recipe for item", Keyboard.KEY_R, "NotEnoughUpdates"); + public final KeyBinding keybindToggleDisplay = new KeyBinding("Toggle NEU overlay", 0, "NotEnoughUpdates"); + public final KeyBinding keybindClosePanes = new KeyBinding("Close NEU panes", 0, "NotEnoughUpdates"); + public final KeyBinding[] keybinds = new KeyBinding[]{keybindGive, keybindFavourite, keybindViewUsages, keybindViewRecipe, keybindToggleDisplay, keybindClosePanes}; + public String viewItemAttemptID = null; public long viewItemAttemptTime = 0; @@ -74,13 +85,7 @@ public class NEUManager { gsonBuilder.registerTypeAdapter(Options.Option.class, Options.createDeserializer()); gson = gsonBuilder.create(); - this.configFile = new File(configLocation, "config.json"); - try { - configFile.createNewFile(); - config = Options.loadFromFile(gson, configFile); - } catch(Exception e) { - config = new Options(); - } + this.loadConfig(); this.itemsLocation = new File(configLocation, "items"); itemsLocation.mkdir(); @@ -171,15 +176,38 @@ public class NEUManager { public class CraftInfo { public boolean fromRecipe = false; + public boolean vanillaItem = false; public float craftCost = -1; } + /** + * Recursively calculates the cost of crafting an item from raw materials. + */ public CraftInfo getCraftCost(String internalname) { if(craftCost.containsKey(internalname)) { return craftCost.get(internalname); } else { CraftInfo ci = new CraftInfo(); + //Removes trailing numbers and underscores, eg. LEAVES_2-3 -> LEAVES + String vanillaName = internalname.split("-")[0]; + int sub = 0; + for(int i=vanillaName.length()-1; i>1; i--) { + char c = vanillaName.charAt(i); + if((int)c >= 48 && (int)c <= 57) { //0-9 + sub++; + } else if(c == '_') { + sub++; + break; + } else { + break; + } + } + vanillaName = vanillaName.substring(0, vanillaName.length()-sub); + if(Item.itemRegistry.getObject(new ResourceLocation(vanillaName)) != null) { + ci.vanillaItem = true; + } + JsonObject auctionInfo = getItemAuctionInfo(internalname); JsonObject bazaarInfo = getBazaarInfo(internalname); @@ -187,7 +215,7 @@ public class NEUManager { float bazaarInstantBuyPrice = bazaarInfo.get("curr_buy").getAsFloat(); ci.craftCost = bazaarInstantBuyPrice; } - if(auctionInfo != null) { + if(auctionInfo != null && !ci.vanillaItem) { //Don't use auction prices for vanilla items cuz people like to transfer money, messing up the cost of vanilla items. float auctionPrice = auctionInfo.get("price").getAsFloat() / auctionInfo.get("count").getAsFloat(); if(ci.craftCost < 0 || auctionPrice < ci.craftCost) { ci.craftCost = auctionPrice; @@ -212,8 +240,10 @@ public class NEUManager { } float compCost = getCraftCost(itemS).craftCost * count; if(compCost < 0) { - craftCost.put(internalname, ci); - return ci; + if(!getCraftCost(itemS).vanillaItem) { //If it's a vanilla item without a cost attached to it, let compCost = 0. + craftCost.put(internalname, ci); + return ci; + } } else { craftPrice += compCost; } @@ -233,8 +263,21 @@ public class NEUManager { config.saveToFile(gson, configFile); } + public void loadConfig() { + this.configFile = new File(configLocation, "config.json"); + try { + configFile.createNewFile(); + config = Options.loadFromFile(gson, configFile); + } catch(Exception e) { + config = new Options(); + } + } + + /** + * Downloads and sets auctionPricesJson from the URL specified by AUCTIONS_PRICE_URL. + */ public void updatePrices() { - if(System.currentTimeMillis() - auctionLastUpdate > 1000*60*30) { //30 minutes + if(System.currentTimeMillis() - auctionLastUpdate > 1000*60*120) { //2 hours craftCost.clear(); System.out.println("UPDATING PRICE INFORMATION"); auctionLastUpdate = System.currentTimeMillis(); @@ -266,23 +309,40 @@ public class NEUManager { return e.getAsJsonObject(); } + /** + * Calculates the cost of enchants + other price modifiers such as pet xp, midas price, etc. + */ public float getCostOfEnchants(String internalname, NBTTagCompound tag) { float costOfEnchants = 0; + if(true) return 0; + JsonObject info = getItemAuctionInfo(internalname); if(info == null || !info.has("price")) { return 0; } + if(!auctionPricesJson.has("ench_prices") || !auctionPricesJson.has("ench_maximums")) { + return 0; + } + JsonObject ench_prices = auctionPricesJson.getAsJsonObject("ench_prices"); + JsonObject ench_maximums = auctionPricesJson.getAsJsonObject("ench_maximums"); + if(!ench_prices.has(internalname) || !ench_maximums.has(internalname)) { + return 0; + } + JsonObject iid_variables = ench_prices.getAsJsonObject(internalname); + float ench_maximum = ench_maximums.get(internalname).getAsFloat(); + + int enchants = 0; float price = getItemAuctionInfo(internalname).get("price").getAsFloat(); if(tag.hasKey("ExtraAttributes")) { NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes"); if(ea.hasKey("enchantments")) { - JsonObject ench_prices = auctionPricesJson.get("ench_prices").getAsJsonObject(); NBTTagCompound enchs = ea.getCompoundTag("enchantments"); for(String ench : enchs.getKeySet()) { + enchants++; int level = enchs.getInteger(ench); - for(Map.Entry entry : ench_prices.entrySet()) { + for(Map.Entry entry : iid_variables.entrySet()) { if(matchEnch(ench, level, entry.getKey())) { costOfEnchants += entry.getValue().getAsJsonObject().get("A").getAsFloat()*price + entry.getValue().getAsJsonObject().get("B").getAsFloat(); @@ -295,15 +355,23 @@ public class NEUManager { return costOfEnchants; } + /** + * Checks whether a certain enchant (ench name + lvl) matches an enchant id + * eg. PROTECTION_GE6 will match -> ench_name = PROTECTION, lvl >= 6 + */ private boolean matchEnch(String ench, int level, String id) { + if(!id.contains(":")) { + return false; + } + String idEnch = id.split(":")[0]; String idLevel = id.split(":")[1]; - if(!ench.equals(idEnch)) { + if(!ench.equalsIgnoreCase(idEnch)) { return false; } - if(String.valueOf(level).equals(idLevel)) { + if(String.valueOf(level).equalsIgnoreCase(idLevel)) { return true; } @@ -582,6 +650,22 @@ public class NEUManager { } } + /** + * Calls search for each query, separated by | + * eg. search(A|B) = search(A) + search(B) + */ + public Set search(String query, boolean multi) { + if(multi) { + Set result = new HashSet<>(); + for(String query2 : query.split("\\|")) { + result.addAll(search(query2)); + } + return result; + } else { + return search(query); + } + } + /** * Returns the name of items which match a certain search query. */ @@ -700,7 +784,7 @@ public class NEUManager { } /** - * Takes an item stack and produces a JsonObject. This is used in the item editor. + * Takes an item stack and produces a JsonObject. */ public JsonObject getJsonForItem(ItemStack stack) { NBTTagCompound tag = stack.getTagCompound() == null ? new NBTTagCompound() : stack.getTagCompound(); @@ -775,9 +859,220 @@ public class NEUManager { writeJson(json, new File(itemsLocation, internalname+".json")); } catch (IOException e) {} - loadItem(internalname); //Collection: Cocoa Beans VII + loadItem(internalname); + } + + /** + * Constructs a GuiItemUsages from the recipe usage data (see #usagesMap) of a given item + */ + public boolean displayGuiItemUsages(String internalName, String text) { + List craftMatrices = new ArrayList<>(); + List results = new ArrayList<>(); + + if(!usagesMap.containsKey(internalName)) { + return false; + } + + for(String internalNameResult : usagesMap.get(internalName)) { + JsonObject item = getItemInformation().get(internalNameResult); + results.add(item); + + if(item != null && item.has("recipe")) { + JsonObject recipe = item.get("recipe").getAsJsonObject(); + + ItemStack[] craftMatrix = new ItemStack[9]; + + String[] x = {"1","2","3"}; + String[] y = {"A","B","C"}; + for(int i=0; i<9; i++) { + String name = y[i/3]+x[i%3]; + String itemS = recipe.get(name).getAsString(); + int count = 1; + if(itemS != null && itemS.split(":").length == 2) { + count = Integer.valueOf(itemS.split(":")[1]); + itemS = itemS.split(":")[0]; + } + JsonObject craft = getItemInformation().get(itemS); + if(craft != null) { + ItemStack stack = jsonToStack(craft); + stack.stackSize = count; + craftMatrix[i] = stack; + } + } + + craftMatrices.add(craftMatrix); + } + } + + if(craftMatrices.size() > 0) { + Minecraft.getMinecraft().thePlayer.sendQueue.addToSendQueue(new C0DPacketCloseWindow( + Minecraft.getMinecraft().thePlayer.openContainer.windowId)); + Minecraft.getMinecraft().displayGuiScreen(new GuiItemUsages(craftMatrices, results, text, this)); + return true; + } + return false; + } + + /** + * Constructs a GuiItemRecipe from the recipe data of a given item. + */ + public boolean displayGuiItemRecipe(String internalName, String text) { + JsonObject item = getItemInformation().get(internalName); + if(item != null && item.has("recipe")) { + JsonObject recipe = item.get("recipe").getAsJsonObject(); + + ItemStack[] craftMatrix = new ItemStack[9]; + + String[] x = {"1","2","3"}; + String[] y = {"A","B","C"}; + for(int i=0; i<9; i++) { + String name = y[i/3]+x[i%3]; + String itemS = recipe.get(name).getAsString(); + int count = 1; + if(itemS != null && itemS.split(":").length == 2) { + count = Integer.valueOf(itemS.split(":")[1]); + itemS = itemS.split(":")[0]; + } + JsonObject craft = getItemInformation().get(itemS); + if(craft != null) { + ItemStack stack = jsonToStack(craft); + stack.stackSize = count; + craftMatrix[i] = stack; + } + } + + Minecraft.getMinecraft().thePlayer.sendQueue.addToSendQueue(new C0DPacketCloseWindow( + Minecraft.getMinecraft().thePlayer.openContainer.windowId)); + Minecraft.getMinecraft().displayGuiScreen(new GuiItemRecipe(craftMatrix, item, text, this)); + return true; + } + return false; + } + + /** + * Will display guiItemRecipe if a player attempted to view the recipe to an item but they didn't have the recipe + * unlocked. See NotEnoughUpdates#onGuiChat for where this method is called. + */ + public boolean failViewItem(String text) { + if(viewItemAttemptID != null && !viewItemAttemptID.isEmpty()) { + if(System.currentTimeMillis() - viewItemAttemptTime < 500) { + return displayGuiItemRecipe(viewItemAttemptID, text); + } + } + return false; + } + + /** + * Downloads a web file, appending some HTML attributes that makes wikia give us the raw wiki syntax. + */ + public File getWebFile(String url) { + File f = new File(configLocation, "tmp/"+Base64.getEncoder().encodeToString(url.getBytes())+".html"); + if(f.exists()) { + return f; + } + + try { + f.getParentFile().mkdirs(); + f.createNewFile(); + f.deleteOnExit(); + } catch (IOException e) { + return null; + } + try (BufferedInputStream inStream = new BufferedInputStream(new URL(url+"?action=raw&templates=expand").openStream()); + FileOutputStream fileOutputStream = new FileOutputStream(f)) { + byte dataBuffer[] = new byte[1024]; + int bytesRead; + while ((bytesRead = inStream.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + return f; + } + + + /** + * Modified from https://www.journaldev.com/960/java-unzip-file-example + */ + private static void unzipIgnoreFirstFolder(String zipFilePath, String destDir) { + File dir = new File(destDir); + // create output directory if it doesn't exist + if(!dir.exists()) dir.mkdirs(); + FileInputStream fis; + //buffer for read and write data to file + byte[] buffer = new byte[1024]; + try { + fis = new FileInputStream(zipFilePath); + ZipInputStream zis = new ZipInputStream(fis); + ZipEntry ze = zis.getNextEntry(); + while(ze != null){ + if(!ze.isDirectory()) { + String fileName = ze.getName(); + fileName = fileName.substring(fileName.split("/")[0].length()+1); + File newFile = new File(destDir + File.separator + fileName); + //create directories for sub directories in zip + new File(newFile.getParent()).mkdirs(); + FileOutputStream fos = new FileOutputStream(newFile); + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + fos.close(); + } + //close this ZipEntry + zis.closeEntry(); + ze = zis.getNextEntry(); + } + //close last ZipEntry + zis.closeEntry(); + zis.close(); + fis.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Modified from https://www.journaldev.com/960/java-unzip-file-example + */ + private static void unzip(InputStream src, File dest) { + //buffer for read and write data to file + byte[] buffer = new byte[1024]; + try { + ZipInputStream zis = new ZipInputStream(src); + ZipEntry ze = zis.getNextEntry(); + while(ze != null){ + if(!ze.isDirectory()) { + String fileName = ze.getName(); + File newFile = new File(dest, fileName); + //create directories for sub directories in zip + new File(newFile.getParent()).mkdirs(); + FileOutputStream fos = new FileOutputStream(newFile); + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + fos.close(); + } + //close this ZipEntry + zis.closeEntry(); + ze = zis.getNextEntry(); + } + //close last ZipEntry + zis.closeEntry(); + zis.close(); + } catch (IOException e) { + e.printStackTrace(); + } } + /** + * From here to the end of the file are various helper functions for creating and writing json files, + * in particular json files representing skyblock item data. + */ public JsonObject createItemJson(String internalname, String itemid, String displayname, String[] lore, String crafttext, String infoType, String[] info, String clickcommand, int damage, NBTTagCompound nbttag) { @@ -884,81 +1179,6 @@ public class NEUManager { return itemMap; } - /** - * Stolen from https://www.journaldev.com/960/java-unzip-file-example - */ - private static void unzipIgnoreFirstFolder(String zipFilePath, String destDir) { - File dir = new File(destDir); - // create output directory if it doesn't exist - if(!dir.exists()) dir.mkdirs(); - FileInputStream fis; - //buffer for read and write data to file - byte[] buffer = new byte[1024]; - try { - fis = new FileInputStream(zipFilePath); - ZipInputStream zis = new ZipInputStream(fis); - ZipEntry ze = zis.getNextEntry(); - while(ze != null){ - if(!ze.isDirectory()) { - String fileName = ze.getName(); - fileName = fileName.substring(fileName.split("/")[0].length()+1); - File newFile = new File(destDir + File.separator + fileName); - //create directories for sub directories in zip - new File(newFile.getParent()).mkdirs(); - FileOutputStream fos = new FileOutputStream(newFile); - int len; - while ((len = zis.read(buffer)) > 0) { - fos.write(buffer, 0, len); - } - fos.close(); - } - //close this ZipEntry - zis.closeEntry(); - ze = zis.getNextEntry(); - } - //close last ZipEntry - zis.closeEntry(); - zis.close(); - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Stolen from https://www.journaldev.com/960/java-unzip-file-example - */ - private static void unzip(InputStream src, File dest) { - //buffer for read and write data to file - byte[] buffer = new byte[1024]; - try { - ZipInputStream zis = new ZipInputStream(src); - ZipEntry ze = zis.getNextEntry(); - while(ze != null){ - if(!ze.isDirectory()) { - String fileName = ze.getName(); - File newFile = new File(dest, fileName); - //create directories for sub directories in zip - new File(newFile.getParent()).mkdirs(); - FileOutputStream fos = new FileOutputStream(newFile); - int len; - while ((len = zis.read(buffer)) > 0) { - fos.write(buffer, 0, len); - } - fos.close(); - } - //close this ZipEntry - zis.closeEntry(); - ze = zis.getNextEntry(); - } - //close last ZipEntry - zis.closeEntry(); - zis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - public ItemStack jsonToStack(JsonObject json) { if(itemstackCache.containsKey(json.get("internalname").getAsString())) { return itemstackCache.get(json.get("internalname").getAsString()).copy(); @@ -982,7 +1202,10 @@ public class NEUManager { } if(json.has("lore")) { - NBTTagCompound display = stack.getTagCompound().getCompoundTag("display"); + NBTTagCompound display = new NBTTagCompound(); + if(stack.getTagCompound() != null && stack.getTagCompound().hasKey("display")) { + display = stack.getTagCompound().getCompoundTag("display"); + } NBTTagList lore = new NBTTagList(); for(JsonElement line : json.get("lore").getAsJsonArray()) { String lineStr = line.getAsString(); @@ -992,7 +1215,7 @@ public class NEUManager { } } display.setTag("Lore", lore); - NBTTagCompound tag = stack.getTagCompound(); + NBTTagCompound tag = stack.getTagCompound() != null ? stack.getTagCompound() : new NBTTagCompound(); tag.setTag("display", display); stack.setTagCompound(tag); } @@ -1002,118 +1225,4 @@ public class NEUManager { return stack; } - public boolean displayGuiItemUsages(String internalName, String text) { - List craftMatrices = new ArrayList<>(); - List results = new ArrayList<>(); - - if(!usagesMap.containsKey(internalName)) { - return false; - } - - for(String internalNameResult : usagesMap.get(internalName)) { - JsonObject item = getItemInformation().get(internalNameResult); - results.add(item); - - if(item != null && item.has("recipe")) { - JsonObject recipe = item.get("recipe").getAsJsonObject(); - - ItemStack[] craftMatrix = new ItemStack[9]; - - String[] x = {"1","2","3"}; - String[] y = {"A","B","C"}; - for(int i=0; i<9; i++) { - String name = y[i/3]+x[i%3]; - String itemS = recipe.get(name).getAsString(); - int count = 1; - if(itemS != null && itemS.split(":").length == 2) { - count = Integer.valueOf(itemS.split(":")[1]); - itemS = itemS.split(":")[0]; - } - JsonObject craft = getItemInformation().get(itemS); - if(craft != null) { - ItemStack stack = jsonToStack(craft); - stack.stackSize = count; - craftMatrix[i] = stack; - } - } - - craftMatrices.add(craftMatrix); - } - } - - if(craftMatrices.size() > 0) { - Minecraft.getMinecraft().displayGuiScreen(new GuiItemUsages(craftMatrices, results, text, this)); - return true; - } - return false; - } - - public boolean displayGuiItemRecipe(String internalName, String text) { - JsonObject item = getItemInformation().get(internalName); - if(item != null && item.has("recipe")) { - JsonObject recipe = item.get("recipe").getAsJsonObject(); - - ItemStack[] craftMatrix = new ItemStack[9]; - - String[] x = {"1","2","3"}; - String[] y = {"A","B","C"}; - for(int i=0; i<9; i++) { - String name = y[i/3]+x[i%3]; - String itemS = recipe.get(name).getAsString(); - int count = 1; - if(itemS != null && itemS.split(":").length == 2) { - count = Integer.valueOf(itemS.split(":")[1]); - itemS = itemS.split(":")[0]; - } - JsonObject craft = getItemInformation().get(itemS); - if(craft != null) { - ItemStack stack = jsonToStack(craft); - stack.stackSize = count; - craftMatrix[i] = stack; - } - } - - Minecraft.getMinecraft().displayGuiScreen(new GuiItemRecipe(craftMatrix, item, text, this)); - return true; - } - return false; - } - - public boolean failViewItem(String text) { - if(viewItemAttemptID != null && !viewItemAttemptID.isEmpty()) { - if(System.currentTimeMillis() - viewItemAttemptTime < 500) { - return displayGuiItemRecipe(viewItemAttemptID, text); - } - } - return false; - } - - public File getWebFile(String url) { - File f = new File(configLocation, "tmp/"+Base64.getEncoder().encodeToString(url.getBytes())+".html"); - if(f.exists()) { - return f; - } - - try { - f.getParentFile().mkdirs(); - f.createNewFile(); - f.deleteOnExit(); - } catch (IOException e) { - return null; - } - try (BufferedInputStream inStream = new BufferedInputStream(new URL(url+"?action=raw&templates=expand").openStream()); - FileOutputStream fileOutputStream = new FileOutputStream(f)) { - byte dataBuffer[] = new byte[1024]; - int bytesRead; - while ((bytesRead = inStream.read(dataBuffer, 0, 1024)) != -1) { - fileOutputStream.write(dataBuffer, 0, bytesRead); - } - } catch (IOException e) { - e.printStackTrace(); - return null; - } - - return f; - } - } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java index e60ca14f..59233882 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java @@ -6,7 +6,6 @@ import io.github.moulberry.notenoughupdates.infopanes.*; import io.github.moulberry.notenoughupdates.itemeditor.NEUItemEditor; import io.github.moulberry.notenoughupdates.util.LerpingFloat; import io.github.moulberry.notenoughupdates.util.LerpingInteger; -import io.github.moulberry.notenoughupdates.util.TexLoc; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.*; import net.minecraft.client.gui.inventory.GuiContainer; @@ -17,14 +16,15 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.resources.model.IBakedModel; import net.minecraft.client.shader.Framebuffer; import net.minecraft.client.shader.Shader; -import net.minecraft.client.shader.ShaderGroup; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.inventory.Slot; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.EnumFacing; import net.minecraft.util.Matrix4f; @@ -34,9 +34,9 @@ import org.apache.commons.lang3.StringUtils; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL14; import java.awt.*; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.NumberFormat; @@ -68,23 +68,19 @@ public class NEUOverlay extends Gui { order_alphabetical_active, order_rarity_active }; + //Various constants used for GUI structure private int searchBarXSize = 200; private final int searchBarYOffset = 10; private final int searchBarYSize = 40; private final int searchBarPadding = 2; - public static final int BOX_PADDING = 15; + private static final int BOX_PADDING = 15; public static final int ITEM_PADDING = 4; public static final int ITEM_SIZE = 16; private Color bg = new Color(90, 90, 140, 50); private Color fg = new Color(100,100,100, 255); - //private String informationPaneTitle; - //private ResourceLocation informationPaneImage = null; - //private String[] informationPane; - //private AtomicInteger webpageAwaitID = new AtomicInteger(-1); - //private boolean configOpen = false; private InfoPane activeInfoPane = null; private TreeSet searchedItems = null; @@ -121,12 +117,17 @@ public class NEUOverlay extends Gui { private ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); + private boolean disabled = false; + public NEUOverlay(NEUManager manager) { this.manager = manager; textField.setFocused(true); textField.setCanLoseFocus(false); } + /** + * Disables searchBarFocus and resets the item pane position. Called whenever NEUOverlay is opened. + */ public void reset() { searchBarHasFocus = false; if(!(searchMode || (manager.config.keepopen.value && itemPaneOpen))) { @@ -136,6 +137,9 @@ public class NEUOverlay extends Gui { } } + /** + * Calls #displayInformationPane with a HTMLInfoPane created from item.info and item.infoType. + */ public void showInfo(JsonObject item) { if(item.has("info") && item.has("infoType")) { JsonArray lore = item.get("info").getAsJsonArray(); @@ -163,6 +167,10 @@ public class NEUOverlay extends Gui { * Handles the mouse input, cancelling the forge event if a NEU gui element is clicked. */ public boolean mouseInput() { + if(disabled) { + return false; + } + int width = scaledresolution.getScaledWidth(); int height = scaledresolution.getScaledHeight(); @@ -208,8 +216,8 @@ public class NEUOverlay extends Gui { if(!clickedItem.get()) { int paneWidth = (int)(width/3*getWidthMult()); int leftSide = (int)(width*getItemPaneOffsetFactor()); - int rightSide = leftSide+paneWidth-BOX_PADDING-getItemBoxXPadding(); - leftSide = leftSide+BOX_PADDING+getItemBoxXPadding(); + int rightSide = leftSide+paneWidth-getBoxPadding()-getItemBoxXPadding(); + leftSide = leftSide+getBoxPadding()+getItemBoxXPadding(); FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; int maxPages = getMaxPages(); @@ -219,7 +227,7 @@ public class NEUOverlay extends Gui { int buttonXSize = (int)Math.min(maxButtonXSize, getSearchBarYSize()*480/160f); int ySize = (int)(buttonXSize/480f*160); int yOffset = (int)((getSearchBarYSize()-ySize)/2f); - int top = BOX_PADDING+yOffset; + int top = getBoxPadding()+yOffset; if(mouseY >= top && mouseY <= top+ySize) { int leftPrev = leftSide-1; @@ -233,16 +241,16 @@ public class NEUOverlay extends Gui { } float sortIconsMinX = (sortIcons.length+orderIcons.length)*(ITEM_SIZE+ITEM_PADDING)+ITEM_SIZE; - float availableX = rightSide-(leftSide+BOX_PADDING+getItemBoxXPadding()); + float availableX = rightSide-leftSide; float sortOrderScaleFactor = Math.min(1, availableX / sortIconsMinX); int scaledITEM_SIZE = (int)(ITEM_SIZE*sortOrderScaleFactor); int scaledItemPaddedSize = (int)((ITEM_SIZE+ITEM_PADDING)*sortOrderScaleFactor); - int iconTop = height-BOX_PADDING-(ITEM_SIZE+scaledITEM_SIZE)/2-1; + int iconTop = height-getBoxPadding()-(ITEM_SIZE+scaledITEM_SIZE)/2-1; if(mouseY >= iconTop && mouseY <= iconTop+scaledITEM_SIZE) { for(int i=0; i= orderIconX && mouseX <= orderIconX+scaledITEM_SIZE) { if(Mouse.getEventButton() == 0) { manager.config.compareMode.value = new Double(i); @@ -289,6 +297,39 @@ public class NEUOverlay extends Gui { } } + + //Quickcommands + int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor(); + if(paddingUnscaled < 1) paddingUnscaled = 1; + int topTextBox = height - searchBarYOffset - getSearchBarYSize(); + + if(Mouse.getEventButtonState() && manager.config.showQuickCommands.value) { + ArrayList quickCommands = manager.config.quickCommands.value; + int bigItemSize = getSearchBarYSize(); + int bigItemPadding = paddingUnscaled*4; + int xStart = width/2 + bigItemPadding/2 - (bigItemSize+bigItemPadding)*quickCommands.size()/2; + int xEnd = width/2 - bigItemPadding/2 + (bigItemSize+bigItemPadding)*quickCommands.size()/2; + int y = topTextBox - bigItemSize - bigItemPadding - paddingUnscaled*2; + + if(mouseY >= y && mouseY <= topTextBox-paddingUnscaled*2) { + if(mouseX > xStart && mouseX < xEnd) { + if((mouseX - xStart)%(bigItemSize+bigItemPadding) < bigItemSize) { + int index = (mouseX - xStart)/(bigItemSize+bigItemPadding); + if(index >= 0 && index < quickCommands.size()) { + String quickCommand = quickCommands.get(index); + if(quickCommand.contains(":")) { + String command = quickCommand.split(":")[0].trim(); + if(command.startsWith("/")) { + NotEnoughUpdates.INSTANCE.sendChatMessage(command); + return true; + } + } + } + } + } + } + } + //Search bar if(mouseX >= width/2 - getSearchBarXSize()/2 && mouseX <= width/2 + getSearchBarXSize()/2) { if(mouseY >= height - searchBarYOffset - getSearchBarYSize() && @@ -310,10 +351,7 @@ public class NEUOverlay extends Gui { } } - int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor(); - int topTextBox = height - searchBarYOffset - getSearchBarYSize(); int iconSize = getSearchBarYSize()+paddingUnscaled*2; - if(paddingUnscaled < 1) paddingUnscaled = 1; if(mouseY > topTextBox - paddingUnscaled && mouseY < topTextBox - paddingUnscaled + iconSize) { if(mouseX > width/2 + getSearchBarXSize()/2 + paddingUnscaled*6 && @@ -344,11 +382,17 @@ public class NEUOverlay extends Gui { return false; } + /** + * Returns searchBarXSize, scaled by 0.8 if gui scale == AUTO. + */ public int getSearchBarXSize() { if(scaledresolution.getScaleFactor()==4) return (int)(searchBarXSize*0.8); return searchBarXSize; } + /** + * Sets the activeInfoPane and sets the target of the infoPaneOffsetFactor to make the infoPane "slide" out. + */ public void displayInformationPane(InfoPane pane) { if(pane == null) { infoPaneOffsetFactor.setTarget(0); @@ -363,79 +407,9 @@ public class NEUOverlay extends Gui { return activeInfoPane; } - /*public void displayInformationPane(String title, String infoType, String[] info) { - scrollHeight.setValue(0); - informationPaneTitle = title; - informationPaneImage = null; - informationPane = null; - - configOpen = false; - - infoPaneOffsetFactor.setTarget(1/3f); - infoPaneOffsetFactor.resetTimer(); - - webpageAwaitID.incrementAndGet(); - - if(info == null || info.length == 0) { - informationPane = new String[]{"\u00A77No additional information."}; - } else { - String joined = StringUtils.join(info, "\n"); - String wiki = null; - String html = null; - if(infoType.equals("TEXT")) { - informationPane = info; - return; - } else if(infoType.equals("WIKI_URL")) { - File f = manager.getWebFile(joined); - if(f == null) { - informationPane = new String[] { EnumChatFormatting.RED+"Failed to load wiki url: "+joined }; - return; - }; - - StringBuilder sb = new StringBuilder(); - try(BufferedReader br = new BufferedReader(new InputStreamReader( - new FileInputStream(f), StandardCharsets.UTF_8))) { - String l; - while((l = br.readLine()) != null){ - sb.append(l).append("\n"); - } - } catch(IOException e) { - informationPane = new String[] { EnumChatFormatting.RED+"Failed to load wiki url: "+joined }; - return; - } - wiki = sb.toString(); - } - - if(infoType.equals("WIKI") || wiki != null) { - if(wiki == null) wiki = joined; - try { - String[] split = wiki.split(""); - wiki = split[split.length - 1]; //Remove everything before infobox - wiki = wiki.split("")[0]; //Remove navbox - wiki = wiki.split(" internalname = new AtomicReference<>(null); @@ -524,16 +521,16 @@ public class NEUOverlay extends Gui { } JsonObject item = manager.getItemInformation().get(internalname.get()); if(item != null) { - if(Keyboard.getEventCharacter() == 'u') { + if(keyPressed == manager.keybindViewUsages.getKeyCode()) { manager.displayGuiItemUsages(internalname.get(), ""); return true; - } else if(Keyboard.getEventCharacter() == 'f') { - toggleRarity(item.get("internalname").getAsString()); + } else if(keyPressed == manager.keybindFavourite.getKeyCode()) { + toggleFavourite(item.get("internalname").getAsString()); return true; - } else if(Keyboard.getEventCharacter() == 'r') { + } else if(keyPressed == manager.keybindViewRecipe.getKeyCode()) { manager.showRecipe(item); return true; - } else if(Keyboard.getEventCharacter() == 'l') { + } else if(keyPressed == manager.keybindGive.getKeyCode()) { if(Minecraft.getMinecraft().thePlayer.capabilities.isCreativeMode) { Minecraft.getMinecraft().thePlayer.inventory.addItemStackToInventory( manager.jsonToStack(item)); @@ -551,7 +548,7 @@ public class NEUOverlay extends Gui { return searchBarHasFocus; //Cancels keyboard events if the search bar has focus } - public void toggleRarity(String internalname) { + public void toggleFavourite(String internalname) { if(getFavourites().contains(internalname)) { getFavourites().remove(internalname); } else { @@ -568,6 +565,17 @@ public class NEUOverlay extends Gui { EnumChatFormatting.GOLD+EnumChatFormatting.BOLD.toString()+"LEGENDARY", EnumChatFormatting.LIGHT_PURPLE+EnumChatFormatting.BOLD.toString()+"SPECIAL", }; + + /** + * Finds the rarity from the lore of an item. + * -1 = UNKNOWN + * 0 = COMMON + * 1 = UNCOMMON + * 2 = RARE + * 3 = EPIC + * 4 = LEGENDARY + * 5 = SPECIAL + */ public int getRarity(JsonArray lore) { for(int i=lore.size()-1; i>=0; i--) { String line = lore.get(i).getAsString(); @@ -581,6 +589,9 @@ public class NEUOverlay extends Gui { return -1; } + /** + * Convenience functions that get various compare/sort modes from the config. + */ private int getCompareMode() { return manager.config.compareMode.value.intValue(); } @@ -594,6 +605,10 @@ public class NEUOverlay extends Gui { return manager.config.favourites.value; } + /** + * Creates an item comparator used to sort the list of items according to the favourite set then compare mode. + * Defaults to alphabetical sorting if the above factors cannot distinguish between two items. + */ private Comparator getItemComparator() { return (o1, o2) -> { //1 (mult) if o1 should appear after o2 @@ -644,6 +659,11 @@ public class NEUOverlay extends Gui { }; } + /** + * Checks whether an item matches a certain type, i.e. whether the item lore ends in "{rarity} {item type}" + * eg. "SHOVEL" will return >0 for "COMMON SHOVEL", "EPIC SHOVEL", etc. + * @return the index of the type that matched, or -1 otherwise. + */ public int checkItemType(JsonArray lore, String... typeMatches) { for(int i=lore.size()-1; i>=0; i--) { String line = lore.get(i).getAsString(); @@ -659,6 +679,9 @@ public class NEUOverlay extends Gui { return -1; } + /** + * Checks whether an item matches the current sort mode. + */ public boolean checkMatchesSort(String internalname, JsonObject item) { if(getSortMode() == SORT_MODE_ALL) { return !internalname.matches(mobRegex); @@ -677,20 +700,44 @@ public class NEUOverlay extends Gui { return true; } + /** + * Clears the current item list, creating a new TreeSet if necessary. + * Adds all items that match the search AND match the sort mode to the current item list. + * Also adds some easter egg items. (Also I'm very upset if you came here to find them :'( ) + */ public void updateSearch() { if(searchedItems==null) searchedItems = new TreeSet<>(getItemComparator()); searchedItems.clear(); searchedItemsArr = null; redrawItems = true; - Set itemsMatch = manager.search(textField.getText()); + Set itemsMatch = manager.search(textField.getText(), true); for(String itemname : itemsMatch) { JsonObject item = manager.getItemInformation().get(itemname); if(checkMatchesSort(itemname, item)) { searchedItems.add(item); } } + switch(textField.getText().toLowerCase().trim()) { + case "nullzee": + searchedItems.add(CustomItems.NULLZEE); + break; + case "rune": + searchedItems.add(CustomItems.RUNE); + break; + case "2b2t": + searchedItems.add(CustomItems.TWOBEETWOTEE); + break; + case "ducttape": + case "ducttapedigger": + searchedItems.add(CustomItems.DUCTTAPE); + break; + } } + /** + * Returns an index-able array containing the elements in searchedItems. + * Whenever searchedItems is updated via the above method, the array is recreated here. + */ public JsonObject[] getSearchedItems() { if(searchedItems==null) { updateSearch(); @@ -706,6 +753,10 @@ public class NEUOverlay extends Gui { return searchedItemsArr; } + /** + * Gets the item in searchedItemArr corresponding to the certain index on the current page. + * @return item, if the item exists. null, otherwise. + */ public JsonObject getSearchedItemPage(int index) { if(index < getSlotsXSize()*getSlotsYSize()) { int actualIndex = index + getSlotsXSize()*getSlotsYSize()*page; @@ -721,7 +772,11 @@ public class NEUOverlay extends Gui { public int getItemBoxXPadding() { int width = scaledresolution.getScaledWidth(); - return (((int)(width/3*getWidthMult())-2*BOX_PADDING)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; + return (((int)(width/3*getWidthMult())-2*getBoxPadding())%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; + } + + public int getBoxPadding() { + return (BOX_PADDING-5)*2/scaledresolution.getScaleFactor()+5; } private abstract class ItemSlotConsumer { @@ -731,7 +786,7 @@ public class NEUOverlay extends Gui { public void iterateItemSlots(ItemSlotConsumer itemSlotConsumer) { int width = scaledresolution.getScaledWidth(); int itemBoxXPadding = getItemBoxXPadding(); - iterateItemSlots(itemSlotConsumer, (int)(width*getItemPaneOffsetFactor())+BOX_PADDING+itemBoxXPadding); + iterateItemSlots(itemSlotConsumer, (int)(width*getItemPaneOffsetFactor())+getBoxPadding()+itemBoxXPadding); } /** @@ -744,12 +799,12 @@ public class NEUOverlay extends Gui { int height = scaledresolution.getScaledHeight(); int paneWidth = (int)(width/3*getWidthMult()); - int itemBoxYPadding = ((height-getSearchBarYSize()-2*BOX_PADDING-ITEM_SIZE-2)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; + int itemBoxYPadding = ((height-getSearchBarYSize()-2*getBoxPadding()-ITEM_SIZE-2)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; - int yStart = BOX_PADDING+getSearchBarYSize()+itemBoxYPadding; + int yStart = getBoxPadding()+getSearchBarYSize()+itemBoxYPadding; int itemBoxXPadding = getItemBoxXPadding(); - int xEnd = xStart+paneWidth-BOX_PADDING*2-ITEM_SIZE-itemBoxXPadding; - int yEnd = height-BOX_PADDING-ITEM_SIZE-2-itemBoxYPadding; + int xEnd = xStart+paneWidth-getBoxPadding()*2-ITEM_SIZE-itemBoxXPadding; + int yEnd = height-getBoxPadding()-ITEM_SIZE-2-itemBoxYPadding; //Render the items, displaying the tooltip if the cursor is over the item int id = 0; @@ -773,9 +828,9 @@ public class NEUOverlay extends Gui { int width = scaledresolution.getScaledWidth(); int paneWidth = (int)(width/3*getWidthMult()); - int itemBoxXPadding = (((int)(width-width*getItemPaneOffsetFactor())-2*BOX_PADDING)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; - int xStart = (int)(width*getItemPaneOffsetFactor())+BOX_PADDING+itemBoxXPadding; - int xEnd = (int)(width*getItemPaneOffsetFactor())+paneWidth-BOX_PADDING-ITEM_SIZE; + int itemBoxXPadding = (((int)(width-width*getItemPaneOffsetFactor())-2*getBoxPadding())%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; + int xStart = (int)(width*getItemPaneOffsetFactor())+getBoxPadding()+itemBoxXPadding; + int xEnd = (int)(width*getItemPaneOffsetFactor())+paneWidth-getBoxPadding()-ITEM_SIZE; return (int)Math.ceil((xEnd - xStart)/((float)(ITEM_SIZE+ITEM_PADDING))); } @@ -786,9 +841,9 @@ public class NEUOverlay extends Gui { public int getSlotsYSize() { int height = scaledresolution.getScaledHeight(); - int itemBoxYPadding = ((height-getSearchBarYSize()-2*BOX_PADDING-ITEM_SIZE-2)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; - int yStart = BOX_PADDING+getSearchBarYSize()+itemBoxYPadding; - int yEnd = height-BOX_PADDING-ITEM_SIZE-2-itemBoxYPadding; + int itemBoxYPadding = ((height-getSearchBarYSize()-2*getBoxPadding()-ITEM_SIZE-2)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; + int yStart = getBoxPadding()+getSearchBarYSize()+itemBoxYPadding; + int yEnd = height-getBoxPadding()-ITEM_SIZE-2-itemBoxYPadding; return (int)Math.ceil((yEnd - yStart)/((float)(ITEM_SIZE+ITEM_PADDING))); } @@ -798,28 +853,14 @@ public class NEUOverlay extends Gui { return (int)Math.ceil(getSearchedItems().length/(float)getSlotsYSize()/getSlotsXSize()); } - /** - * Takes in the x and y coordinates of a slot and returns the id of that slot. - */ - /*public int getSlotId(int x, int y) { - int width = scaledresolution.getScaledWidth(); - int height = scaledresolution.getScaledHeight(); - - int itemBoxXPadding = (((int)(width-width*getItemPaneOffsetFactor())-2*BOX_PADDING)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; - int itemBoxYPadding = ((height-getSearchBarYSize()-2*BOX_PADDING-ITEM_SIZE-2)%(ITEM_SIZE+ITEM_PADDING)+ITEM_PADDING)/2; - - int xStart = (int)(width*getItemPaneOffsetFactor())+BOX_PADDING+itemBoxXPadding; - int yStart = BOX_PADDING+getSearchBarYSize()+itemBoxYPadding; - - int xIndex = (x-xStart)/(ITEM_SIZE+ITEM_PADDING); - int yIndex = (y-yStart)/(ITEM_SIZE+ITEM_PADDING); - return xIndex + yIndex*getSlotsXSize(); - }*/ - public int getSearchBarYSize() { return Math.max(searchBarYSize/scaledresolution.getScaleFactor(), ITEM_SIZE); } + /** + * Renders the top navigation bar, can be used by InfoPane implementations (such as SettingsInfoPane). + * Renders "prev" button, index/maxIndex string, "next" button. + */ public void renderNavElement(int leftSide, int rightSide, int maxPages, int page, String name) { FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; @@ -830,11 +871,7 @@ public class NEUOverlay extends Gui { int buttonXSize = (int)Math.min(maxButtonXSize, getSearchBarYSize()*480/160f); int ySize = (int)(buttonXSize/480f*160); int yOffset = (int)((getSearchBarYSize()-ySize)/2f); - int top = BOX_PADDING+yOffset; - - /*drawRect(leftSide-1, top, - rightSide+1, - top+ySize, fg.getRGB());*/ + int top = getBoxPadding()+yOffset; int leftPressed = 0; int rightPressed = 0; @@ -907,6 +944,9 @@ public class NEUOverlay extends Gui { public float yaw = 0; public float pitch = 20; + /** + * Renders an entity onto the GUI at a certain x and y position. + */ private void renderEntity(float posX, float posY, float scale, String name, Class... classes) { EntityLivingBase[] entities = new EntityLivingBase[classes.length]; try { @@ -972,6 +1012,10 @@ public class NEUOverlay extends Gui { GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); } + /** + * Renders black squares over the inventory to indicate items that do not match a specific search. (When searchMode + * is enabled) + */ public void renderOverlay(int mouseX, int mouseY) { if(searchMode && textField.getText().length() > 0) { GuiContainer inv = (GuiContainer) Minecraft.getMinecraft().currentScreen; @@ -1008,6 +1052,13 @@ public class NEUOverlay extends Gui { Shader blurShaderVert = null; Framebuffer blurOutputVert = null; + /** + * Creates a projection matrix that projects from our coordinate space [0->width; 0->height] to OpenGL coordinate + * space [-1 -> 1; 1 -> -1] (Note: flipped y-axis). + * + * This is so that we can render to and from the framebuffer in a way that is familiar to us, instead of needing to + * apply scales and translations manually. + */ private Matrix4f createProjectionMatrix(int width, int height) { Matrix4f projMatrix = new Matrix4f(); projMatrix.setIdentity(); @@ -1021,12 +1072,18 @@ public class NEUOverlay extends Gui { return projMatrix; } + /** + * Renders whatever is currently in the Minecraft framebuffer to our two framebuffers, applying a horizontal + * and vertical blur separately in order to significantly save computation time. + * This is only possible if framebuffers are supported by the system, so this method will exit prematurely + * if framebuffers are not available. (Apple machines, for example, have poor framebuffer support). + */ private double lastBgBlurFactor = 5; private void blurBackground() { int width = Minecraft.getMinecraft().displayWidth; int height = Minecraft.getMinecraft().displayHeight; - if(manager.config.bgBlurFactor.value <= 0) return; + if(manager.config.bgBlurFactor.value <= 0 || !OpenGlHelper.isFramebufferEnabled()) return; if(blurOutputHorz == null) { blurOutputHorz = new Framebuffer(width, height, false); @@ -1079,8 +1136,12 @@ public class NEUOverlay extends Gui { } } + /** + * Renders a subsection of the blurred framebuffer on to the corresponding section of the screen. + * Essentially, this method will "blur" the background inside the bounds specified by [x->x+blurWidth, y->y+blurHeight] + */ public void renderBlurredBackground(int width, int height, int x, int y, int blurWidth, int blurHeight) { - if(manager.config.bgBlurFactor.value <= 0) return; + if(manager.config.bgBlurFactor.value <= 0 || !OpenGlHelper.isFramebufferEnabled()) return; int f = scaledresolution.getScaleFactor(); float uMin = x/(float)width; @@ -1099,9 +1160,12 @@ public class NEUOverlay extends Gui { int guiScaleLast = 0; /** - * Renders the search bar, item selection (right) and item info (left) gui elements. + * Renders the search bar, quick commands, item selection (right) and item info (left) gui elements. */ public void render(int mouseX, int mouseY, boolean hoverInv) { + if(disabled) { + return; + } FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); int width = scaledresolution.getScaledWidth(); @@ -1162,109 +1226,12 @@ public class NEUOverlay extends Gui { GlStateManager.disableLighting(); - /** - * Search bar - */ - int paddingUnscaled = searchBarPadding/scaledresolution.getScaleFactor(); - if(paddingUnscaled < 1) paddingUnscaled = 1; - - int topTextBox = height - searchBarYOffset - getSearchBarYSize(); - - /*Minecraft.getMinecraft().getTextureManager().bindTexture(logo_bg); - GlStateManager.color(1f, 1f, 1f, 1f); - Utils.drawTexturedRect((width)/2-37, - height - searchBarYOffset - getSearchBarYSize()-30, - 74, 54); - GlStateManager.bindTexture(0);*/ - - //Search bar background - drawRect(width/2 - getSearchBarXSize()/2 - paddingUnscaled, - topTextBox - paddingUnscaled, - width/2 + getSearchBarXSize()/2 + paddingUnscaled, - height - searchBarYOffset + paddingUnscaled, searchMode ? Color.YELLOW.getRGB() : Color.WHITE.getRGB()); - drawRect(width/2 - getSearchBarXSize()/2, - topTextBox, - width/2 + getSearchBarXSize()/2, - height - searchBarYOffset, Color.BLACK.getRGB()); - - /*Minecraft.getMinecraft().getTextureManager().bindTexture(logo_fg); - GlStateManager.color(1f, 1f, 1f, 1f); - Utils.drawTexturedRect((width)/2-37, - height - searchBarYOffset - getSearchBarYSize()-27, - 74, 54); - GlStateManager.bindTexture(0);*/ - - //Settings - int iconSize = getSearchBarYSize()+paddingUnscaled*2; - Minecraft.getMinecraft().getTextureManager().bindTexture(settings); - drawRect(width/2 - getSearchBarXSize()/2 - paddingUnscaled*6 - iconSize, - topTextBox - paddingUnscaled, - width/2 - getSearchBarXSize()/2 - paddingUnscaled*6, - topTextBox - paddingUnscaled + iconSize, Color.WHITE.getRGB()); - - drawRect(width/2 - getSearchBarXSize()/2 - paddingUnscaled*5 - iconSize, - topTextBox, - width/2 - getSearchBarXSize()/2 - paddingUnscaled*7, - topTextBox - paddingUnscaled*2 + iconSize, Color.GRAY.getRGB()); - GlStateManager.color(1f, 1f, 1f, 1f); - Utils.drawTexturedRect(width/2 - getSearchBarXSize()/2 - paddingUnscaled*6 - iconSize, topTextBox - paddingUnscaled, iconSize, iconSize); - GlStateManager.bindTexture(0); - - //Help - Minecraft.getMinecraft().getTextureManager().bindTexture(help); - drawRect(width/2 + getSearchBarXSize()/2 + paddingUnscaled*6, - topTextBox - paddingUnscaled, - width/2 + getSearchBarXSize()/2 + paddingUnscaled*6 + iconSize, - topTextBox - paddingUnscaled + iconSize, Color.WHITE.getRGB()); - - drawRect(width/2 + getSearchBarXSize()/2 + paddingUnscaled*7, - topTextBox, - width/2 + getSearchBarXSize()/2 + paddingUnscaled*5 + iconSize, - topTextBox - paddingUnscaled*2 + iconSize, Color.GRAY.getRGB()); - GlStateManager.color(1f, 1f, 1f, 1f); - Utils.drawTexturedRect(width/2 + getSearchBarXSize()/2 + paddingUnscaled*7, topTextBox, - iconSize-paddingUnscaled*2, iconSize-paddingUnscaled*2); - GlStateManager.bindTexture(0); - - //Search bar text - fr.drawString(textField.getText(), width/2 - getSearchBarXSize()/2 + 5, - topTextBox+(getSearchBarYSize()-8)/2, Color.WHITE.getRGB()); - - //Determines position of cursor. Cursor blinks on and off every 500ms. - if(searchBarHasFocus && System.currentTimeMillis()%1000>500) { - String textBeforeCursor = textField.getText().substring(0, textField.getCursorPosition()); - int textBeforeCursorWidth = fr.getStringWidth(textBeforeCursor); - drawRect(width/2 - getSearchBarXSize()/2 + 5 + textBeforeCursorWidth, - topTextBox+(getSearchBarYSize()-8)/2-1, - width/2 - getSearchBarXSize()/2 + 5 + textBeforeCursorWidth+1, - topTextBox+(getSearchBarYSize()-8)/2+9, Color.WHITE.getRGB()); - } - - String selectedText = textField.getSelectedText(); - if(!selectedText.isEmpty()) { - int selectionWidth = fr.getStringWidth(selectedText); - - int leftIndex = Math.min(textField.getCursorPosition(), textField.getSelectionEnd()); - String textBeforeSelection = textField.getText().substring(0, leftIndex); - int textBeforeSelectionWidth = fr.getStringWidth(textBeforeSelection); - - drawRect(width/2 - getSearchBarXSize()/2 + 5 + textBeforeSelectionWidth, - topTextBox+(getSearchBarYSize()-8)/2-1, - width/2 - getSearchBarXSize()/2 + 5 + textBeforeSelectionWidth + selectionWidth, - topTextBox+(getSearchBarYSize()-8)/2+9, Color.LIGHT_GRAY.getRGB()); - - fr.drawString(selectedText, - width/2 - getSearchBarXSize()/2 + 5 + textBeforeSelectionWidth, - topTextBox+(getSearchBarYSize()-8)/2, Color.BLACK.getRGB()); - } - - /** * Item selection (right) gui element rendering */ int paneWidth = (int)(width/3*getWidthMult()); int leftSide = (int)(width*getItemPaneOffsetFactor()); - int rightSide = leftSide+paneWidth-BOX_PADDING-getItemBoxXPadding(); + int rightSide = leftSide+paneWidth-getBoxPadding()-getItemBoxXPadding(); //Tab @@ -1288,31 +1255,31 @@ public class NEUOverlay extends Gui { if(itemPaneOffsetFactor.getValue() < 1) { renderBlurredBackground(width, height, - leftSide+BOX_PADDING-5, BOX_PADDING-5, - paneWidth-BOX_PADDING*2+10, height-BOX_PADDING*2+10); + leftSide+getBoxPadding()-5, getBoxPadding()-5, + paneWidth-getBoxPadding()*2+10, height-getBoxPadding()*2+10); - drawRect(leftSide+BOX_PADDING-5, BOX_PADDING-5, - leftSide+paneWidth-BOX_PADDING+5, height-BOX_PADDING+5, bg.getRGB()); + drawRect(leftSide+getBoxPadding()-5, getBoxPadding()-5, + leftSide+paneWidth-getBoxPadding()+5, height-getBoxPadding()+5, bg.getRGB()); - renderNavElement(leftSide+BOX_PADDING+getItemBoxXPadding(), rightSide, getMaxPages(), page+1, + renderNavElement(leftSide+getBoxPadding()+getItemBoxXPadding(), rightSide, getMaxPages(), page+1, scaledresolution.getScaleFactor()<4?"Page: ":""); //Sort bar - drawRect(leftSide+BOX_PADDING+getItemBoxXPadding()-1, - height-BOX_PADDING-ITEM_SIZE-2, + drawRect(leftSide+getBoxPadding()+getItemBoxXPadding()-1, + height-getBoxPadding()-ITEM_SIZE-2, rightSide+1, - height-BOX_PADDING, fgCustomOpacity.getRGB()); + height-getBoxPadding(), fgCustomOpacity.getRGB()); float sortIconsMinX = (sortIcons.length+orderIcons.length)*(ITEM_SIZE+ITEM_PADDING)+ITEM_SIZE; - float availableX = rightSide-(leftSide+BOX_PADDING+getItemBoxXPadding()); + float availableX = rightSide-(leftSide+getBoxPadding()+getItemBoxXPadding()); float sortOrderScaleFactor = Math.min(1, availableX / sortIconsMinX); int scaledITEM_SIZE = (int)(ITEM_SIZE*sortOrderScaleFactor); int scaledItemPaddedSize = (int)((ITEM_SIZE+ITEM_PADDING)*sortOrderScaleFactor); - int iconTop = height-BOX_PADDING-(ITEM_SIZE+scaledITEM_SIZE)/2-1; + int iconTop = height-getBoxPadding()-(ITEM_SIZE+scaledITEM_SIZE)/2-1; for(int i=0; i quickCommands = manager.config.quickCommands.value; + int bigItemSize = getSearchBarYSize(); + int bigItemPadding = paddingUnscaled*4; + int x = width/2 + bigItemPadding/2 - (bigItemSize+bigItemPadding)*quickCommands.size()/2; + int y = topTextBox - bigItemSize - bigItemPadding - paddingUnscaled*2; + + for(String quickCommand : quickCommands) { + if(!quickCommand.contains(":")) { + continue; + } + String display = quickCommand.split(":")[1]; + ItemStack render = null; + float extraScale = 1; + if(display.length() > 20) { //Custom head + render = new ItemStack(Items.skull, 1, 3); + NBTTagCompound nbt = new NBTTagCompound(); + NBTTagCompound skullOwner = new NBTTagCompound(); + NBTTagCompound properties = new NBTTagCompound(); + NBTTagList textures = new NBTTagList(); + NBTTagCompound textures_0 = new NBTTagCompound(); + + + String uuid = UUID.nameUUIDFromBytes(display.getBytes()).toString(); + skullOwner.setString("Id", uuid); + skullOwner.setString("Name", uuid); + + textures_0.setString("Value", display); + textures.appendTag(textures_0); + + properties.setTag("textures", textures); + skullOwner.setTag("Properties", properties); + nbt.setTag("SkullOwner", skullOwner); + render.setTagCompound(nbt); + + extraScale = 1.3f; + } else if(manager.getItemInformation().containsKey(display)) { + render = manager.jsonToStack(manager.getItemInformation().get(display)); + } else { + Item item = Item.itemRegistry.getObject(new ResourceLocation(display.toLowerCase())); + if(item != null) { + render = new ItemStack(item); + } + } + if(render != null) { + Minecraft.getMinecraft().getTextureManager().bindTexture(item_mask); + GlStateManager.color(1, 1, 1, 1); + Utils.drawTexturedRect(x - paddingUnscaled, y - paddingUnscaled, + bigItemSize + paddingUnscaled*2, bigItemSize + paddingUnscaled*2, GL11.GL_NEAREST); + GlStateManager.color(fg.getRed() / 255f,fg.getGreen() / 255f, + fg.getBlue() / 255f, fg.getAlpha() / 255f); + Utils.drawTexturedRect(x, y, bigItemSize, bigItemSize, GL11.GL_NEAREST); + + float itemScale = bigItemSize/(float)ITEM_SIZE*extraScale; + GlStateManager.pushMatrix(); + GlStateManager.scale(itemScale, itemScale, 1); + GlStateManager.translate((x-(extraScale-1)*bigItemSize/2) /itemScale, + (y-(extraScale-1)*bigItemSize/2)/itemScale, 0f); + Utils.drawItemStack(render, 0, 0); + GlStateManager.popMatrix(); + } + x += bigItemSize + bigItemPadding; + } + } + + //Settings + int iconSize = getSearchBarYSize()+paddingUnscaled*2; + Minecraft.getMinecraft().getTextureManager().bindTexture(settings); + drawRect(width/2 - getSearchBarXSize()/2 - paddingUnscaled*6 - iconSize, + topTextBox - paddingUnscaled, + width/2 - getSearchBarXSize()/2 - paddingUnscaled*6, + topTextBox - paddingUnscaled + iconSize, Color.WHITE.getRGB()); + + drawRect(width/2 - getSearchBarXSize()/2 - paddingUnscaled*5 - iconSize, + topTextBox, + width/2 - getSearchBarXSize()/2 - paddingUnscaled*7, + topTextBox - paddingUnscaled*2 + iconSize, Color.GRAY.getRGB()); + GlStateManager.color(1f, 1f, 1f, 1f); + Utils.drawTexturedRect(width/2 - getSearchBarXSize()/2 - paddingUnscaled*6 - iconSize, topTextBox - paddingUnscaled, iconSize, iconSize); + GlStateManager.bindTexture(0); + + //Help + Minecraft.getMinecraft().getTextureManager().bindTexture(help); + drawRect(width/2 + getSearchBarXSize()/2 + paddingUnscaled*6, + topTextBox - paddingUnscaled, + width/2 + getSearchBarXSize()/2 + paddingUnscaled*6 + iconSize, + topTextBox - paddingUnscaled + iconSize, Color.WHITE.getRGB()); + + drawRect(width/2 + getSearchBarXSize()/2 + paddingUnscaled*7, + topTextBox, + width/2 + getSearchBarXSize()/2 + paddingUnscaled*5 + iconSize, + topTextBox - paddingUnscaled*2 + iconSize, Color.GRAY.getRGB()); + GlStateManager.color(1f, 1f, 1f, 1f); + Utils.drawTexturedRect(width/2 + getSearchBarXSize()/2 + paddingUnscaled*7, topTextBox, + iconSize-paddingUnscaled*2, iconSize-paddingUnscaled*2); + GlStateManager.bindTexture(0); + + //Search bar text + fr.drawString(textField.getText(), width/2 - getSearchBarXSize()/2 + 5, + topTextBox+(getSearchBarYSize()-8)/2, Color.WHITE.getRGB()); + + //Determines position of cursor. Cursor blinks on and off every 500ms. + if(searchBarHasFocus && System.currentTimeMillis()%1000>500) { + String textBeforeCursor = textField.getText().substring(0, textField.getCursorPosition()); + int textBeforeCursorWidth = fr.getStringWidth(textBeforeCursor); + drawRect(width/2 - getSearchBarXSize()/2 + 5 + textBeforeCursorWidth, + topTextBox+(getSearchBarYSize()-8)/2-1, + width/2 - getSearchBarXSize()/2 + 5 + textBeforeCursorWidth+1, + topTextBox+(getSearchBarYSize()-8)/2+9, Color.WHITE.getRGB()); + } + + String selectedText = textField.getSelectedText(); + if(!selectedText.isEmpty()) { + int selectionWidth = fr.getStringWidth(selectedText); + + int leftIndex = Math.min(textField.getCursorPosition(), textField.getSelectionEnd()); + String textBeforeSelection = textField.getText().substring(0, leftIndex); + int textBeforeSelectionWidth = fr.getStringWidth(textBeforeSelection); + + drawRect(width/2 - getSearchBarXSize()/2 + 5 + textBeforeSelectionWidth, + topTextBox+(getSearchBarYSize()-8)/2-1, + width/2 - getSearchBarXSize()/2 + 5 + textBeforeSelectionWidth + selectionWidth, + topTextBox+(getSearchBarYSize()-8)/2+9, Color.LIGHT_GRAY.getRGB()); + + fr.drawString(selectedText, + width/2 - getSearchBarXSize()/2 + 5 + textBeforeSelectionWidth, + topTextBox+(getSearchBarYSize()-8)/2, Color.BLACK.getRGB()); + } + //Render tooltip JsonObject json = tooltipToDisplay.get(); if(json != null) { @@ -1446,10 +1571,17 @@ public class NEUOverlay extends Gui { } } + /** + * Used in SettingsInfoPane to redraw the items when a setting changes. + */ public void redrawItems() { redrawItems = true; } + /** + * Sets the current page and marks that the itemsPane should be redrawn + * @param page + */ public void setPage(int page) { this.page = page; redrawItems = true; @@ -1457,6 +1589,10 @@ public class NEUOverlay extends Gui { private Framebuffer[] itemFramebuffers = new Framebuffer[2]; + /** + * Checks whether the screen size has changed, if so it reconstructs the itemPane framebuffer and marks that the + * itemPane should be redrawn. + */ private void checkFramebufferSizes(int width, int height) { int sw = width*scaledresolution.getScaleFactor(); int sh = height*scaledresolution.getScaleFactor(); @@ -1483,7 +1619,11 @@ public class NEUOverlay extends Gui { Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(true); } - + /** + * Renders all items to a framebuffer so that it can be reused later, drastically improving performance. + * Unfortunately using this feature will mean that animated textures will not work, but oh well. + * Mojang please optimize item rendering thanks. + */ private void renderItemsToImage(int width, int height, Color fgFavourite2, Color fgFavourite, Color fgCustomOpacity, boolean items, boolean entities) { int sw = width*scaledresolution.getScaleFactor(); @@ -1491,7 +1631,7 @@ public class NEUOverlay extends Gui { GL11.glPushMatrix(); prepareFramebuffer(itemFramebuffers[0], sw, sh); - renderItems(10, items, entities); + renderItems(10, items, entities, false); cleanupFramebuffer(itemFramebuffers[0], sw, sh); GL11.glPopMatrix(); @@ -1504,6 +1644,11 @@ public class NEUOverlay extends Gui { private static final ResourceLocation RES_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png"); + /** + * Renders the framebuffer created by #renderItemsToImage to the screen. + * itemRenderOffset is a magic number that makes the z-level of the rendered items equal to the z-level of + * the item glint overlay model, meaning that a depthFunc of GL_EQUAL can correctly render on to the item. + */ float itemRenderOffset = 7.5001f; private void renderItemsFromImage(int xOffset, int width, int height) { if(itemFramebuffers[0] != null && itemFramebuffers[1] != null) { @@ -1521,6 +1666,14 @@ public class NEUOverlay extends Gui { } } + /** + * Renders the enchant overlay, since only the items have the specific z-offset of 7.5001, this will only apply + * the enchant overlay to the actual items and not anything else. + * + * (I tried very hard to replicate the enchant rendering overlay code from vanilla, but I couldn't get it to + * work without rendering with the "ITEM" vertex model like in vanilla, so I choose to render an arbitrary 2D + * item. If a texture pack sets a custom 3D model for an apple, this will probably break.) + */ private void renderEnchOverlay() { ItemStack stack = new ItemStack(Items.apple); IBakedModel model = Minecraft.getMinecraft().getRenderItem().getItemModelMesher() @@ -1533,49 +1686,48 @@ public class NEUOverlay extends Gui { GL11.glTranslatef(0, 0, -7.5001f+itemRenderOffset); iterateItemSlots(new ItemSlotConsumer() { public void consume(int x, int y, int id) { - JsonObject json = getSearchedItemPage(id); - if (json == null || !manager.jsonToStack(json).hasEffect()) { - return; - } - - GlStateManager.pushMatrix(); - GlStateManager.enableRescaleNormal(); - GlStateManager.enableAlpha(); - GlStateManager.alphaFunc(516, 0.1F); - GlStateManager.enableBlend(); + JsonObject json = getSearchedItemPage(id); + if (json == null || !manager.jsonToStack(json).hasEffect()) { + return; + } - GlStateManager.disableLighting(); + GlStateManager.pushMatrix(); + GlStateManager.enableRescaleNormal(); + GlStateManager.enableAlpha(); + GlStateManager.alphaFunc(516, 0.1F); + GlStateManager.enableBlend(); - GlStateManager.translate(x, y, 0); - GlStateManager.scale(16f, 16f, 16f); + GlStateManager.disableLighting(); - GlStateManager.depthMask(false); - GlStateManager.depthFunc(GL11.GL_EQUAL); - GlStateManager.blendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); - GlStateManager.matrixMode(5890); - GlStateManager.pushMatrix(); - GlStateManager.scale(8.0F, 8.0F, 8.0F); - GlStateManager.translate(f, 0.0F, 0.0F); - GlStateManager.rotate(-50.0F, 0.0F, 0.0F, 1.0F); + GlStateManager.translate(x, y, 0); + GlStateManager.scale(16f, 16f, 16f); - renderModel(model, -8372020, null); + GlStateManager.depthMask(false); + GlStateManager.depthFunc(GL11.GL_EQUAL); + GlStateManager.blendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + GlStateManager.matrixMode(5890); + GlStateManager.pushMatrix(); + GlStateManager.scale(8.0F, 8.0F, 8.0F); + GlStateManager.translate(f, 0.0F, 0.0F); + GlStateManager.rotate(-50.0F, 0.0F, 0.0F, 1.0F); - GlStateManager.popMatrix(); - GlStateManager.pushMatrix(); - GlStateManager.scale(8.0F, 8.0F, 8.0F); - GlStateManager.translate(-f1, 0.0F, 0.0F); - GlStateManager.rotate(10.0F, 0.0F, 0.0F, 1.0F); + renderModel(model, -8372020, null); - renderModel(model, -8372020, null); + GlStateManager.popMatrix(); + GlStateManager.pushMatrix(); + GlStateManager.scale(8.0F, 8.0F, 8.0F); + GlStateManager.translate(-f1, 0.0F, 0.0F); + GlStateManager.rotate(10.0F, 0.0F, 0.0F, 1.0F); - GlStateManager.popMatrix(); - GlStateManager.matrixMode(5888); - GlStateManager.blendFunc(770, 771); - GlStateManager.depthFunc(515); - GlStateManager.depthMask(true); + renderModel(model, -8372020, null); - GlStateManager.popMatrix(); + GlStateManager.popMatrix(); + GlStateManager.matrixMode(5888); + GlStateManager.blendFunc(770, 771); + GlStateManager.depthFunc(515); + GlStateManager.depthMask(true); + GlStateManager.popMatrix(); } }); GlStateManager.disableBlend(); @@ -1610,44 +1762,47 @@ public class NEUOverlay extends Gui { } } + /** + * Renders all the item backgrounds, either squares or squircles. + */ private void renderItemBackgrounds(Color fgFavourite2, Color fgFavourite, Color fgCustomOpacity) { if(fgCustomOpacity.getAlpha() == 0) return; iterateItemSlots(new ItemSlotConsumer() { public void consume(int x, int y, int id) { - JsonObject json = getSearchedItemPage(id); - if (json == null) { - return; - } + JsonObject json = getSearchedItemPage(id); + if (json == null) { + return; + } - Minecraft.getMinecraft().getTextureManager().bindTexture(item_mask); - if (getFavourites().contains(json.get("internalname").getAsString())) { - if(manager.config.itemStyle.value) { - GlStateManager.color(fgFavourite2.getRed() / 255f, fgFavourite2.getGreen() / 255f, - fgFavourite2.getBlue() / 255f, fgFavourite2.getAlpha() / 255f); - Utils.drawTexturedRect(x - 1, y - 1, ITEM_SIZE + 2, ITEM_SIZE + 2, GL11.GL_NEAREST); + Minecraft.getMinecraft().getTextureManager().bindTexture(item_mask); + if (getFavourites().contains(json.get("internalname").getAsString())) { + if(manager.config.itemStyle.value) { + GlStateManager.color(fgFavourite2.getRed() / 255f, fgFavourite2.getGreen() / 255f, + fgFavourite2.getBlue() / 255f, fgFavourite2.getAlpha() / 255f); + Utils.drawTexturedRect(x - 1, y - 1, ITEM_SIZE + 2, ITEM_SIZE + 2, GL11.GL_NEAREST); - GlStateManager.color(fgFavourite.getRed() / 255f, fgFavourite.getGreen() / 255f, - fgFavourite.getBlue() / 255f, fgFavourite.getAlpha() / 255f); - Utils.drawTexturedRect(x, y, ITEM_SIZE, ITEM_SIZE, GL11.GL_NEAREST); - } else { - drawRect(x-1, y-1, x+ITEM_SIZE+1, y+ITEM_SIZE+1, fgFavourite2.getRGB()); - drawRect(x, y, x+ITEM_SIZE, y+ITEM_SIZE, fgFavourite.getRGB()); - } + GlStateManager.color(fgFavourite.getRed() / 255f, fgFavourite.getGreen() / 255f, + fgFavourite.getBlue() / 255f, fgFavourite.getAlpha() / 255f); + Utils.drawTexturedRect(x, y, ITEM_SIZE, ITEM_SIZE, GL11.GL_NEAREST); } else { - if(manager.config.itemStyle.value) { - GlStateManager.color(fgCustomOpacity.getRed() / 255f, fgCustomOpacity.getGreen() / 255f, - fgCustomOpacity.getBlue() / 255f, fgCustomOpacity.getAlpha() / 255f); - Utils.drawTexturedRect(x - 1, y - 1, ITEM_SIZE + 2, ITEM_SIZE + 2, GL11.GL_NEAREST); - } else { - drawRect(x-1, y-1, x+ITEM_SIZE+1, y+ITEM_SIZE+1, fgCustomOpacity.getRGB()); - } + drawRect(x-1, y-1, x+ITEM_SIZE+1, y+ITEM_SIZE+1, fgFavourite2.getRGB()); + drawRect(x, y, x+ITEM_SIZE, y+ITEM_SIZE, fgFavourite.getRGB()); } - GlStateManager.bindTexture(0); + } else { + if(manager.config.itemStyle.value) { + GlStateManager.color(fgCustomOpacity.getRed() / 255f, fgCustomOpacity.getGreen() / 255f, + fgCustomOpacity.getBlue() / 255f, fgCustomOpacity.getAlpha() / 255f); + Utils.drawTexturedRect(x - 1, y - 1, ITEM_SIZE + 2, ITEM_SIZE + 2, GL11.GL_NEAREST); + } else { + drawRect(x-1, y-1, x+ITEM_SIZE+1, y+ITEM_SIZE+1, fgCustomOpacity.getRGB()); + } + } + GlStateManager.bindTexture(0); } }, 10); } - private void renderItems(int xStart, boolean items, boolean entities) { + private void renderItems(int xStart, boolean items, boolean entities, boolean glint) { iterateItemSlots(new ItemSlotConsumer() { public void consume(int x, int y, int id) { JsonObject json = getSearchedItemPage(id); @@ -1687,7 +1842,11 @@ public class NEUOverlay extends Gui { } else { if(!items) return; ItemStack stack = manager.jsonToStack(json); - Utils.drawItemStackWithoutGlint(stack, x, y); + if(glint) { + Utils.drawItemStack(stack, x, y); + } else { + Utils.drawItemStackWithoutGlint(stack, x, y); + } } } }, xStart); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 28b35a0b..b4b48db5 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -11,6 +11,7 @@ import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.client.settings.KeyBinding; import net.minecraft.inventory.ContainerChest; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; @@ -27,6 +28,7 @@ import net.minecraftforge.client.event.GuiOpenEvent; import net.minecraftforge.client.event.GuiScreenEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; @@ -36,6 +38,8 @@ import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; import javax.swing.*; +import java.awt.*; +import java.awt.datatransfer.StringSelection; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -52,7 +56,9 @@ public class NotEnoughUpdates { public static final String MODID = "notenoughupdates"; public static final String VERSION = "1.0.0"; - private NEUManager manager; + public static NotEnoughUpdates INSTANCE = null; + + public NEUManager manager; private NEUOverlay overlay; private NEUIO neuio; @@ -77,8 +83,13 @@ public class NotEnoughUpdates { return s; } + /** + * Instantiates NEUIo, NEUManager and NEUOverlay instances. Registers keybinds and adds a shutdown hook to clear tmp folder. + * @param event + */ @EventHandler public void preinit(FMLPreInitializationEvent event) { + INSTANCE = this; MinecraftForge.EVENT_BUS.register(this); File f = new File(event.getModConfigurationDirectory(), "notenoughupdates"); @@ -88,6 +99,10 @@ public class NotEnoughUpdates { manager.loadItemInformation(); overlay = new NEUOverlay(manager); + for(KeyBinding kb : manager.keybinds) { + ClientRegistry.registerKeyBinding(kb); + } + Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { File tmp = new File(f, "tmp"); @@ -135,6 +150,10 @@ public class NotEnoughUpdates { } } + /** + * If the last chat messages was sent >200ms ago, sends the message. + * If the last chat message was sent <200 ago, will cache the message for #onTick to handle. + */ public void sendChatMessage(String message) { if (System.currentTimeMillis() - lastChatMessage > CHAT_MSG_COOLDOWN) { lastChatMessage = System.currentTimeMillis(); @@ -145,6 +164,10 @@ public class NotEnoughUpdates { } } + /** + * Will send the cached message from #sendChatMessage when at least 200ms has passed since the last message. + * This is used in order to prevent the mod spamming messages. + */ @EventHandler public void onTick(TickEvent.ClientTickEvent event) { if(currChatMessage != null && System.currentTimeMillis() - lastChatMessage > CHAT_MSG_COOLDOWN) { @@ -154,16 +177,32 @@ public class NotEnoughUpdates { } } + /** + * When opening a GuiContainer, will reset the overlay and load the config. + * When closing a GuiContainer, will save the config. + * Also includes a dev feature used for automatically acquiring crafting information from the "Crafting Table" GUI. + */ AtomicBoolean missingRecipe = new AtomicBoolean(false); @SubscribeEvent public void onGuiOpen(GuiOpenEvent event) { - if(event.gui != null) { - System.out.println("2"); + //OPEN + if(Minecraft.getMinecraft().currentScreen == null + && event.gui instanceof GuiContainer) { + overlay.reset(); + manager.loadConfig(); + } + //CLOSE + if(Minecraft.getMinecraft().currentScreen instanceof GuiContainer + && event.gui == null) { + try { + manager.saveConfig(); + } catch(IOException e) {} + } + if(event.gui != null && manager.config.dev.value) { if(event.gui instanceof GuiChest) { GuiChest eventGui = (GuiChest) event.gui; ContainerChest cc = (ContainerChest) eventGui.inventorySlots; IInventory lower = cc.getLowerChestInventory(); - System.out.println("3"); ses.schedule(() -> { if(Minecraft.getMinecraft().currentScreen != event.gui) { return; @@ -258,19 +297,12 @@ public class NotEnoughUpdates { return; } } - //OPEN - if(Minecraft.getMinecraft().currentScreen == null - && event.gui instanceof GuiContainer) { - overlay.reset(); - } - //CLOSE - if(Minecraft.getMinecraft().currentScreen != null && event.gui == null) { - try { - manager.saveConfig(); - } catch(IOException e) {} - } } + /** + * 1) When receiving "You are playing on profile" messages, will set the current profile. + * 2) When a /viewrecipe command fails (i.e. player does not have recipe unlocked, will open the custom recipe GUI) + */ @SubscribeEvent public void onGuiChat(ClientChatReceivedEvent e) { String r = null; @@ -295,6 +327,17 @@ public class NotEnoughUpdates { } } + /** + * Sets hoverInv and focusInv variables, representing whether the NEUOverlay should render behind the inventory when + * (hoverInv == true) and whether mouse/kbd inputs shouldn't be sent to NEUOverlay (focusInv == true). + * + * If hoverInv is true, will render the overlay immediately (resulting in the inventory being drawn over the GUI) + * If hoverInv is false, the overlay will render in #onGuiScreenDraw (resulting in the GUI being drawn over the inv) + * + * All of this only matters if players are using gui scale auto which may result in the inventory being drawn + * over the various panes. + * @param event + */ @SubscribeEvent public void onGuiBackgroundDraw(GuiScreenEvent.BackgroundDrawnEvent event) { if(event.gui instanceof GuiContainer && isOnSkyblock()) { @@ -331,6 +374,11 @@ public class NotEnoughUpdates { } } + /** + * Will draw the NEUOverlay over the inventory if focusInv == false. (z-translation of 300 is so that NEUOverlay + * will draw over Items in the inventory (which render at a z value of about 250)) + * @param event + */ @SubscribeEvent public void onGuiScreenDraw(GuiScreenEvent.DrawScreenEvent.Post event) { if(event.gui instanceof GuiContainer && isOnSkyblock()) { @@ -343,6 +391,11 @@ public class NotEnoughUpdates { } } + /** + * Sends a mouse event to NEUOverlay if the inventory isn't hovered AND focused. + * Will also cancel the event if if NEUOverlay#mouseInput returns true. + * @param event + */ @SubscribeEvent public void onGuiScreenMouse(GuiScreenEvent.MouseInputEvent.Pre event) { if(event.gui instanceof GuiContainer && !(hoverInv && focusInv) && isOnSkyblock()) { @@ -354,13 +407,22 @@ public class NotEnoughUpdates { ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); + /** + * Sends a kbd event to NEUOverlay, cancelling if NEUOverlay#keyboardInput returns true. + * Also includes a dev function used for creating custom named json files with recipes. + */ boolean started = false; @SubscribeEvent public void onGuiScreenKeyboard(GuiScreenEvent.KeyboardInputEvent.Pre event) { - if(manager.config.enableItemEditing.value && Minecraft.getMinecraft().theWorld != null && + if(event.gui instanceof GuiContainer && isOnSkyblock()) { + if(overlay.keyboardInput(focusInv)) { + event.setCanceled(true); + } + } + if(manager.config.dev.value && manager.config.enableItemEditing.value && Minecraft.getMinecraft().theWorld != null && Keyboard.getEventKey() == Keyboard.KEY_O && Keyboard.getEventKeyState()) { GuiScreen gui = Minecraft.getMinecraft().currentScreen; - if(gui != null && gui instanceof GuiChest) { + if(gui instanceof GuiChest) { GuiChest eventGui = (GuiChest) event.gui; ContainerChest cc = (ContainerChest) eventGui.inventorySlots; IInventory lower = cc.getLowerChestInventory(); @@ -466,15 +528,9 @@ public class NotEnoughUpdates { ses.schedule(checker, 1000, TimeUnit.MILLISECONDS); }*/ - if(event.gui instanceof GuiContainer && isOnSkyblock()) { - if(overlay.keyboardInput(focusInv)) { - event.setCanceled(true); - } - } } /** - * This was code leftover from testing but it ended up in the final mod so I guess its staying here. * This makes it so that holding LCONTROL while hovering over an item with NBT will show the NBT of the item. * @param event */ @@ -557,6 +613,10 @@ public class NotEnoughUpdates { } } event.toolTip.add(sb.toString()); + if(Keyboard.isKeyDown(Keyboard.KEY_H)) { + StringSelection selection = new StringSelection(sb.toString()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, selection); + } } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/DevInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/DevInfoPane.java index 0abca240..61344237 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/DevInfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/DevInfoPane.java @@ -1,7 +1,9 @@ package io.github.moulberry.notenoughupdates.infopanes; +import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import io.github.moulberry.notenoughupdates.NEUManager; import io.github.moulberry.notenoughupdates.NEUOverlay; import io.github.moulberry.notenoughupdates.Utils; @@ -13,6 +15,8 @@ import net.minecraft.util.ResourceLocation; import org.lwjgl.input.Keyboard; import java.awt.*; +import java.io.*; +import java.nio.charset.StandardCharsets; import java.util.*; import java.util.List; import java.util.concurrent.Executors; @@ -23,6 +27,10 @@ import java.util.concurrent.atomic.AtomicInteger; public class DevInfoPane extends TextInfoPane { + /** + * Provides some dev functions used to help with adding new items/detecting missing items. + */ + public DevInfoPane(NEUOverlay overlay, NEUManager manager) { super(overlay, manager, "Dev", ""); text = getText(); @@ -30,6 +38,25 @@ public class DevInfoPane extends TextInfoPane { private String getText() { String text = ""; + + /*for(Map.Entry item : manager.getItemInformation().entrySet()) { + if(!item.getValue().has("infoType") || item.getValue().get("infoType").getAsString().isEmpty()) { + text += item.getKey() + "\n"; + } + }*/ + for(String s : manager.neuio.getRemovedItems(manager.getItemInformation().keySet())) { + text += s + "\n"; + } + + if(true) return text; + + /*for(Map.Entry item : manager.getItemInformation().entrySet()) { + if(!item.getValue().has("infoType") || item.getValue().get("infoType").getAsString().isEmpty()) { + text += item.getKey() + "\n"; + } + }*/ + //if(true) return text; + for(Map.Entry entry : manager.getAuctionPricesJson().get("prices").getAsJsonObject().entrySet()) { if(!manager.getItemInformation().keySet().contains(entry.getKey())) { if(entry.getKey().contains("-")) { @@ -49,11 +76,82 @@ public class DevInfoPane extends TextInfoPane { ScheduledExecutorService ses = Executors.newScheduledThreadPool(1); @Override - public void keyboardInput() { + public boolean keyboardInput() { if(Keyboard.isKeyDown(Keyboard.KEY_J)) { running.set(!running.get()); - if(running.get()) { + for(Map.Entry item : manager.getItemInformation().entrySet()) { + /*if(!item.getValue().has("infoType") || item.getValue().get("infoType").getAsString().isEmpty()) { + if(item.getValue().has("info") && item.getValue().get("info").getAsJsonArray().size()>0) { + item.getValue().addProperty("infoType", "WIKI_URL"); + try { + manager.writeJsonDefaultDir(item.getValue(), item.getKey()+".json"); + } catch(IOException e){} + manager.loadItem(item.getKey()); + } + }*/ + /*if(item.getKey().startsWith("PET_ITEM_")) { + item.getValue().addProperty("infoType", "WIKI_URL"); + JsonArray array = new JsonArray(); + array.add(new JsonPrimitive("https://hypixel-skyblock.fandom.com/wiki/Pet_Items")); + item.getValue().add("info", array); + try { + manager.writeJsonDefaultDir(item.getValue(), item.getKey()+".json"); + } catch(IOException e){} + manager.loadItem(item.getKey()); + }*/ + /*if(!item.getValue().has("infoType") || item.getValue().get("infoType").getAsString().isEmpty()) { + //String prettyName = + + String itemS = item.getKey().split("-")[0].split(";")[0]; + StringBuilder prettyName = new StringBuilder(); + boolean capital = true; + for(int i=0; i add = new ArrayList<>(); for(Map.Entry item : manager.getItemInformation().entrySet()) { if(item.getValue().has("recipe")) { @@ -74,7 +172,7 @@ public class DevInfoPane extends TextInfoPane { ses.schedule(this, 1000L, TimeUnit.MILLISECONDS); } }, 1000L, TimeUnit.MILLISECONDS); - } + }*/ } /*if(Keyboard.isKeyDown(Keyboard.KEY_J) && !running) { running = true; @@ -102,5 +200,6 @@ public class DevInfoPane extends TextInfoPane { } }, 1000L, TimeUnit.MILLISECONDS); }*/ + return false; } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/FlipperInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/FlipperInfoPane.java index 49cc5389..152d0628 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/FlipperInfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/FlipperInfoPane.java @@ -21,6 +21,10 @@ import java.util.List; public class FlipperInfoPane extends InfoPane { + /** + * Not currently used in BETA-1.6 + */ + protected String title; protected String text; @@ -46,7 +50,7 @@ public class FlipperInfoPane extends InfoPane { int leftSide = rightSide - paneWidth; int titleLen = fr.getStringWidth(title); - fr.drawString(title, (leftSide+rightSide-titleLen)/2, overlay.BOX_PADDING + 5, + fr.drawString(title, (leftSide+rightSide-titleLen)/2, overlay.getBoxPadding() + 5, Color.WHITE.getRGB()); int y = 0; @@ -73,8 +77,8 @@ public class FlipperInfoPane extends InfoPane { fr.drawString("Incl. Stackables: ", x, y, Color.WHITE.getRGB()); drawButton(x, y, false); - drawRect(leftSide+overlay.BOX_PADDING-5, overlay.BOX_PADDING-5, - rightSide-overlay.BOX_PADDING+5, height-overlay.BOX_PADDING+5, bg.getRGB()); + drawRect(leftSide+overlay.getBoxPadding()-5, overlay.getBoxPadding()-5, + rightSide-overlay.getBoxPadding()+5, height-overlay.getBoxPadding()+5, bg.getRGB()); } private void drawButton(int x, int y, boolean enabled) { @@ -87,8 +91,8 @@ public class FlipperInfoPane extends InfoPane { } - public void keyboardInput() { - + public boolean keyboardInput() { + return false; } private int renderParagraph(int width, int height, int startY, String text, Color bg) { @@ -100,9 +104,9 @@ public class FlipperInfoPane extends InfoPane { int yOff = 0; for(String line : text.split("\n")) { - yOff += Utils.renderStringTrimWidth(line, fr, false,leftSide+overlay.BOX_PADDING + 5, - startY+overlay.BOX_PADDING + 10 + yOff, - width*1/3-overlay.BOX_PADDING*2-10, Color.WHITE.getRGB(), -1); + yOff += Utils.renderStringTrimWidth(line, fr, false,leftSide+overlay.getBoxPadding() + 5, + startY+overlay.getBoxPadding() + 10 + yOff, + width*1/3-overlay.getBoxPadding()*2-10, Color.WHITE.getRGB(), -1); yOff += 16; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/HTMLInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/HTMLInfoPane.java index 6469ff1c..ddf8d962 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/HTMLInfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/HTMLInfoPane.java @@ -44,6 +44,9 @@ public class HTMLInfoPane extends TextInfoPane { private int imageHeight = 0; private int imageWidth = 0; + /** + * Creates a wiki model and sets the configuration to work with hypixel-skyblock wikia. + */ static { Configuration conf = new Configuration(); conf.addTokenTag("img", new HTMLTag("img")); @@ -81,6 +84,9 @@ public class HTMLInfoPane extends TextInfoPane { }; } + /** + * Takes a wiki url, uses NEUManager#getWebFile to download the web file and passed that in to #createFromWiki + */ public static HTMLInfoPane createFromWikiUrl(NEUOverlay overlay, NEUManager manager, String name, String wikiUrl) { File f = manager.getWebFile(wikiUrl); if(f == null) { @@ -100,6 +106,12 @@ public class HTMLInfoPane extends TextInfoPane { return createFromWiki(overlay, manager, name, sb.toString()); } + /** + * Takes raw wikia code and uses Bliki to generate HTML. Lot's of shennanigans to get it to render appropriately. + * Honestly, I could have just downloaded the raw HTML of the wiki page and displayed that but I wanted + * a more permanent solution that can be abstracted to work with arbitrary wiki codes (eg. moulberry.github.io/ + * files/neu_help.html). + */ public static HTMLInfoPane createFromWiki(NEUOverlay overlay, NEUManager manager, String name, String wiki) { String[] split = wiki.split(""); wiki = split[split.length - 1]; //Remove everything before infobox @@ -123,6 +135,11 @@ public class HTMLInfoPane extends TextInfoPane { return new HTMLInfoPane(overlay, manager, name, html); } + /** + * Uses the wkhtmltoimage command-line tool to generate an image from the HTML code. This + * generation is done asynchronously as sometimes it can take up to 10 seconds for more + * complex webpages. + */ public HTMLInfoPane(NEUOverlay overlay, NEUManager manager, String name, String html) { super(overlay, manager, name, ""); this.title = name; @@ -237,6 +254,9 @@ public class HTMLInfoPane extends TextInfoPane { } } + /** + * Renders a background, title and the image created in the ctor (if it has been generated). + */ @Override public void render(int width, int height, Color bg, Color fg, ScaledResolution scaledresolution, int mouseX, int mouseY) { if(imageTemp != null && imageTexture == null) { @@ -258,38 +278,39 @@ public class HTMLInfoPane extends TextInfoPane { int leftSide = rightSide - paneWidth; int titleLen = fr.getStringWidth(title); - fr.drawString(title, (leftSide+rightSide-titleLen)/2, overlay.BOX_PADDING + 5, Color.WHITE.getRGB()); + fr.drawString(title, (leftSide+rightSide-titleLen)/2, overlay.getBoxPadding() + 5, Color.WHITE.getRGB()); - drawRect(leftSide+overlay.BOX_PADDING-5, overlay.BOX_PADDING-5, rightSide-overlay.BOX_PADDING+5, - height-overlay.BOX_PADDING+5, bg.getRGB()); + drawRect(leftSide+overlay.getBoxPadding()-5, overlay.getBoxPadding()-5, rightSide-overlay.getBoxPadding()+5, + height-overlay.getBoxPadding()+5, bg.getRGB()); - int imageW = paneWidth - overlay.BOX_PADDING*2; + int imageW = paneWidth - overlay.getBoxPadding()*2; float scaleF = IMAGE_WIDTH*ZOOM_FACTOR/(float)imageW; Minecraft.getMinecraft().getTextureManager().bindTexture(imageTexture); GlStateManager.color(1f, 1f, 1f, 1f); - if(height-overlay.BOX_PADDING*3 < imageHeight/scaleF) { - if(scrollHeight.getValue() > imageHeight/scaleF-height+overlay.BOX_PADDING*3) { - scrollHeight.setValue((int)(imageHeight/scaleF-height+overlay.BOX_PADDING*3)); + if(height-overlay.getBoxPadding()*3 < imageHeight/scaleF) { + if(scrollHeight.getValue() > imageHeight/scaleF-height+overlay.getBoxPadding()*3) { + scrollHeight.setValue((int)(imageHeight/scaleF-height+overlay.getBoxPadding()*3)); } int yScroll = scrollHeight.getValue(); float vMin = yScroll/(imageHeight/scaleF); - float vMax = (yScroll+height-overlay.BOX_PADDING*3)/(imageHeight/scaleF); - Utils.drawTexturedRect(leftSide+overlay.BOX_PADDING, overlay.BOX_PADDING*2, imageW, - height-overlay.BOX_PADDING*3, + float vMax = (yScroll+height-overlay.getBoxPadding()*3)/(imageHeight/scaleF); + Utils.drawTexturedRect(leftSide+overlay.getBoxPadding(), overlay.getBoxPadding()*2, imageW, + height-overlay.getBoxPadding()*3, 0, 1, vMin, vMax); } else { scrollHeight.setValue(0); - Utils.drawTexturedRect(leftSide+overlay.BOX_PADDING, overlay.BOX_PADDING*2, imageW, + Utils.drawTexturedRect(leftSide+overlay.getBoxPadding(), overlay.getBoxPadding()*2, imageW, (int)(imageHeight/scaleF)); } GlStateManager.bindTexture(0); } @Override - public void keyboardInput() { + public boolean keyboardInput() { + return false; } @Override diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/InfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/InfoPane.java index 74321081..2b87fdbc 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/InfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/InfoPane.java @@ -24,21 +24,21 @@ public abstract class InfoPane extends Gui { public abstract void mouseInput(int width, int height, int mouseX, int mouseY, boolean mouseDown); - public abstract void keyboardInput(); + public abstract boolean keyboardInput(); public void renderDefaultBackground(int width, int height, Color bg) { int paneWidth = (int)(width/3*overlay.getWidthMult()); int rightSide = (int)(width*overlay.getInfoPaneOffsetFactor()); int leftSide = rightSide - paneWidth; - int boxLeft = leftSide + overlay.BOX_PADDING - 5; - int boxRight = rightSide - overlay.BOX_PADDING + 5; + int boxLeft = leftSide + overlay.getBoxPadding() - 5; + int boxRight = rightSide - overlay.getBoxPadding() + 5; overlay.renderBlurredBackground(width, height, - boxLeft, overlay.BOX_PADDING-5, - boxRight-boxLeft, height-overlay.BOX_PADDING*2+10); - drawRect(boxLeft, overlay.BOX_PADDING - 5, boxRight, - height - overlay.BOX_PADDING + 5, bg.getRGB()); + boxLeft, overlay.getBoxPadding()-5, + boxRight-boxLeft, height-overlay.getBoxPadding()*2+10); + drawRect(boxLeft, overlay.getBoxPadding() - 5, boxRight, + height - overlay.getBoxPadding() + 5, bg.getRGB()); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/QOLInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/QOLInfoPane.java index bf1a987e..e847a01c 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/QOLInfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/QOLInfoPane.java @@ -29,6 +29,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class QOLInfoPane extends ScrollableInfoPane { + + /** + * Not currently used in BETA-1.6 + */ + private LinkedHashMap accessoryMap = new LinkedHashMap<>(); private LinkedHashMap allTalismans = new LinkedHashMap<>(); private List recommended = new ArrayList<>(); @@ -231,11 +236,11 @@ public class QOLInfoPane extends ScrollableInfoPane { int rightSide = (int)(width*overlay.getInfoPaneOffsetFactor()); int leftSide = rightSide - paneWidth; - int y=overlay.BOX_PADDING+10; + int y=overlay.getBoxPadding()+10; y += renderParagraph(width, height, y, "Current Accessories"); ItemStack display = null; - int x=leftSide+overlay.BOX_PADDING+5; + int x=leftSide+overlay.getBoxPadding()+5; for(Map.Entry entry : accessoryMap.entrySet()) { if(mouseX > x && mouseX < x+16) { if(mouseY > y && mouseY < y+16) { @@ -246,8 +251,8 @@ public class QOLInfoPane extends ScrollableInfoPane { drawRect(x, y, x+16, y+16, fg.getRGB()); Utils.drawItemStack(entry.getValue(), x, y); x += 20; - if(x + 20 + (leftSide+overlay.BOX_PADDING+5) > paneWidth) { - x=leftSide+overlay.BOX_PADDING+5; + if(x + 20 + (leftSide+overlay.getBoxPadding()+5) > paneWidth) { + x=leftSide+overlay.getBoxPadding()+5; y+=20; } } @@ -258,7 +263,7 @@ public class QOLInfoPane extends ScrollableInfoPane { y+=10; - x=leftSide+overlay.BOX_PADDING+5; + x=leftSide+overlay.getBoxPadding()+5; for(Map.Entry entry : allTalismans.entrySet()) { if(accessoryMap.containsKey(entry.getKey())) { continue; @@ -272,8 +277,8 @@ public class QOLInfoPane extends ScrollableInfoPane { drawRect(x, y, x+16, y+16, fg.getRGB()); Utils.drawItemStack(manager.jsonToStack(entry.getValue()), x, y); x += 20; - if(x + 20 + (leftSide+overlay.BOX_PADDING+5) > paneWidth) { - x=leftSide+overlay.BOX_PADDING+5; + if(x + 20 + (leftSide+overlay.getBoxPadding()+5) > paneWidth) { + x=leftSide+overlay.getBoxPadding()+5; y+=20; } } @@ -281,7 +286,7 @@ public class QOLInfoPane extends ScrollableInfoPane { y+=20; y += renderParagraph(width, height, y, "Recommended Accessory Upgrades"); - x=leftSide+overlay.BOX_PADDING+5; + x=leftSide+overlay.getBoxPadding()+5; for(JsonObject json : recommended) { if(mouseX > x && mouseX < x+16) { if(mouseY > y && mouseY < y+16) { @@ -292,8 +297,8 @@ public class QOLInfoPane extends ScrollableInfoPane { drawRect(x, y, x+16, y+16, fg.getRGB()); Utils.drawItemStack(manager.jsonToStack(json), x, y); x += 20; - if(x + 20 + (leftSide+overlay.BOX_PADDING+5) > paneWidth) { - x=leftSide+overlay.BOX_PADDING+5; + if(x + 20 + (leftSide+overlay.getBoxPadding()+5) > paneWidth) { + x=leftSide+overlay.getBoxPadding()+5; y+=20; } } @@ -317,8 +322,8 @@ public class QOLInfoPane extends ScrollableInfoPane { } } - public void keyboardInput() { - + public boolean keyboardInput() { + return false; } @@ -331,9 +336,9 @@ public class QOLInfoPane extends ScrollableInfoPane { int yOff = 0; for(String line : text.split("\n")) { - yOff += Utils.renderStringTrimWidth(line, fr, false,leftSide+overlay.BOX_PADDING + 5, + yOff += Utils.renderStringTrimWidth(line, fr, false,leftSide+overlay.getBoxPadding() + 5, startY + yOff, - width*1/3-overlay.BOX_PADDING*2-10, Color.WHITE.getRGB(), -1); + width*1/3-overlay.getBoxPadding()*2-10, Color.WHITE.getRGB(), -1); yOff += 16; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/SettingsInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/SettingsInfoPane.java index 31f6dca9..2fccbf9e 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/SettingsInfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/SettingsInfoPane.java @@ -47,7 +47,7 @@ public class SettingsInfoPane extends InfoPane { if(page > maxPages-1) page = maxPages-1; if(page < 0) page = 0; - overlay.renderNavElement(leftSide+overlay.BOX_PADDING, rightSide-overlay.BOX_PADDING, + overlay.renderNavElement(leftSide+overlay.getBoxPadding(), rightSide-overlay.getBoxPadding(), maxPages,page+1,"Settings: "); AtomicReference> textToDisplay = new AtomicReference<>(null); @@ -69,8 +69,6 @@ public class SettingsInfoPane extends InfoPane { tileWidth-(int)(16*mult), new Color(100,255,150).getRGB(), 3); } - if(Keyboard.isKeyDown(Keyboard.KEY_H)) return; - if(option.value instanceof Boolean) { GlStateManager.color(1f, 1f, 1f, 1f); Minecraft.getMinecraft().getTextureManager().bindTexture(((Boolean)option.value) ? on : off); @@ -164,8 +162,8 @@ public class SettingsInfoPane extends InfoPane { int paneWidth = (int)(width/3*overlay.getWidthMult()); int rightSide = (int)(width*overlay.getInfoPaneOffsetFactor()); int leftSide = rightSide - paneWidth; - rightSide -= overlay.BOX_PADDING; - leftSide += overlay.BOX_PADDING; + rightSide -= overlay.getBoxPadding(); + leftSide += overlay.getBoxPadding(); FontRenderer fr = Minecraft.getMinecraft().fontRendererObj; float maxStrLen = fr.getStringWidth(EnumChatFormatting.BOLD+"Settings: " + maxPages + "/" + maxPages); @@ -173,7 +171,7 @@ public class SettingsInfoPane extends InfoPane { int buttonXSize = (int)Math.min(maxButtonXSize, overlay.getSearchBarYSize()*480/160f); int ySize = (int)(buttonXSize/480f*160); int yOffset = (int)((overlay.getSearchBarYSize()-ySize)/2f); - int top = overlay.BOX_PADDING+yOffset; + int top = overlay.getBoxPadding()+yOffset; if(mouseY >= top && mouseY <= top+ySize) { int leftPrev = leftSide-1; @@ -188,7 +186,8 @@ public class SettingsInfoPane extends InfoPane { } } - public void keyboardInput() { + public boolean keyboardInput() { + AtomicBoolean ret = new AtomicBoolean(false); iterateSettingTile(new SettingsTileConsumer() { @Override public void consume(int x, int y, int tileWidth, int tileHeight, Options.Option option) { @@ -201,10 +200,12 @@ public class SettingsInfoPane extends InfoPane { if(tf.getFocus()) { tf.keyTyped(Keyboard.getEventCharacter(), Keyboard.getEventKey()); onTextfieldChange(tf, option); + ret.set(true); } } } }); + return ret.get(); } private abstract static class SettingsTileConsumer { @@ -222,10 +223,10 @@ public class SettingsInfoPane extends InfoPane { int rightSide = (int)(width*overlay.getInfoPaneOffsetFactor()); int leftSide = rightSide - paneWidth; - int boxLeft = leftSide+overlay.BOX_PADDING-5; - int boxRight = rightSide-overlay.BOX_PADDING+5; + int boxLeft = leftSide+overlay.getBoxPadding()-5; + int boxRight = rightSide-overlay.getBoxPadding()+5; - int boxBottom = height - overlay.BOX_PADDING + 5; + int boxBottom = height - overlay.getBoxPadding() + 5; int boxWidth = boxRight-boxLeft; int tilePadding = 7; @@ -235,7 +236,7 @@ public class SettingsInfoPane extends InfoPane { maxPages=1; int currPage=0; int x=0; - int y=tilePadding+overlay.BOX_PADDING+overlay.getSearchBarYSize(); + int y=tilePadding+overlay.getBoxPadding()+overlay.getSearchBarYSize(); for(int i=0; i boxBottom) { x=0; - y=tilePadding+overlay.BOX_PADDING+overlay.getSearchBarYSize(); + y=tilePadding+overlay.getBoxPadding()+overlay.getSearchBarYSize(); currPage++; maxPages = currPage+1; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/TextInfoPane.java b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/TextInfoPane.java index 388719d0..1072ca4f 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/infopanes/TextInfoPane.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/infopanes/TextInfoPane.java @@ -29,30 +29,30 @@ public class TextInfoPane extends ScrollableInfoPane { int titleLen = fr.getStringWidth(title); int yScroll = -scrollHeight.getValue(); - fr.drawString(title, (leftSide+rightSide-titleLen)/2, yScroll+overlay.BOX_PADDING + 5, + fr.drawString(title, (leftSide+rightSide-titleLen)/2, yScroll+overlay.getBoxPadding() + 5, Color.WHITE.getRGB()); int yOff = 20; for(String line : text.split("\n")) { - yOff += Utils.renderStringTrimWidth(line, fr, false,leftSide+overlay.BOX_PADDING + 5, - yScroll+overlay.BOX_PADDING + 10 + yOff, - width*1/3-overlay.BOX_PADDING*2-10, Color.WHITE.getRGB(), -1); + yOff += Utils.renderStringTrimWidth(line, fr, false,leftSide+overlay.getBoxPadding() + 5, + yScroll+overlay.getBoxPadding() + 10 + yOff, + width*1/3-overlay.getBoxPadding()*2-10, Color.WHITE.getRGB(), -1); yOff += 16; } - int top = overlay.BOX_PADDING - 5; + int top = overlay.getBoxPadding() - 5; int totalBoxHeight = yOff+14; - int bottom = Math.max(top+totalBoxHeight, height-overlay.BOX_PADDING+5); + int bottom = Math.max(top+totalBoxHeight, height-overlay.getBoxPadding()+5); - if(scrollHeight.getValue() > top+totalBoxHeight-(height-overlay.BOX_PADDING+5)) { - scrollHeight.setValue(top+totalBoxHeight-(height-overlay.BOX_PADDING+5)); + if(scrollHeight.getValue() > top+totalBoxHeight-(height-overlay.getBoxPadding()+5)) { + scrollHeight.setValue(top+totalBoxHeight-(height-overlay.getBoxPadding()+5)); } - drawRect(leftSide+overlay.BOX_PADDING-5, yScroll+overlay.BOX_PADDING-5, - rightSide-overlay.BOX_PADDING+5, yScroll+bottom, bg.getRGB()); + drawRect(leftSide+overlay.getBoxPadding()-5, yScroll+overlay.getBoxPadding()-5, + rightSide-overlay.getBoxPadding()+5, yScroll+bottom, bg.getRGB()); } - public void keyboardInput() { - + public boolean keyboardInput() { + return false; } public void mouseInput(int width, int height, int mouseX, int mouseY, boolean mouseDown) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinInventoryEffectRenderer.java b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinInventoryEffectRenderer.java index 88ffe16e..524c9ffb 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinInventoryEffectRenderer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/mixins/MixinInventoryEffectRenderer.java @@ -1,4 +1,28 @@ package io.github.moulberry.notenoughupdates.mixins; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.Utils; +import net.minecraft.client.renderer.InventoryEffectRenderer; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + + +@Mixin({InventoryEffectRenderer.class}) public class MixinInventoryEffectRenderer { + + @ModifyVariable(method="updateActivePotionEffects", at=@At(value="STORE")) + public boolean hasVisibleEffect_updateActivePotionEffects(boolean hasVisibleEffect) { + if(NotEnoughUpdates.INSTANCE.manager.config.hidePotionEffect.value && + NotEnoughUpdates.INSTANCE.isOnSkyblock()) { + return false; + } else { + return hasVisibleEffect; + } + } + } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/Options.java b/src/main/java/io/github/moulberry/notenoughupdates/options/Options.java index 6c7a70a0..5eae3886 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/Options.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/Options.java @@ -14,6 +14,11 @@ import java.util.List; public class Options { + /** + * Ok, I'll be honest. I wrote this class without looking too much in to how to make proper serializable + * variables with defaults values/etc. It works. I'm happy. + */ + public Option enableItemEditing = new Option( false, "Enable Item Editing", @@ -24,6 +29,16 @@ public class Options { "Only Show On Skyblock", false, "GUI Overlay only appears when you are playing Skyblock."); + public Option hidePotionEffect = new Option( + true, + "Hide Potion Effect", + false, + "If true, the potion effects will not show in the inventory GUI."); + public Option showQuickCommands = new Option( + true, + "Quick Commands", + false, + "If true, shows quick commands above search bar."); public Option advancedPriceInfo = new Option( false, "Advanced Price Information", @@ -35,7 +50,7 @@ public class Options { false, "Caches the drawn itempane, drastically improving performance. However, animated textures will not work."); public Option bgBlurFactor = new Option( - 5.0, + 10.0, "Background Blur Factor", false, "Changes the strength of pane background blur. 0-50."); @@ -103,12 +118,28 @@ public class Options { "Favourites", false, "Favourites"); + public Option> quickCommands = new Option( + createDefaultQuickCommands(), + "Quick Commands", + false, + "Quick Commands"); + + private ArrayList createDefaultQuickCommands() { + ArrayList arr = new ArrayList<>(); + arr.add("/warp home:eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzljODg4MWU0MjkxNWE5ZDI5YmI2MWExNmZiMjZkMDU5OTEzMjA0ZDI2NWRmNWI0MzliM2Q3OTJhY2Q1NiJ9fX0="); + arr.add("/warp hub:eyJ0aW1lc3RhbXAiOjE1NTkyMTU0MTY5MDksInByb2ZpbGVJZCI6IjQxZDNhYmMyZDc0OTQwMGM5MDkwZDU0MzRkMDM4MzFiIiwicHJvZmlsZU5hbWUiOiJNZWdha2xvb24iLCJzaWduYXR1cmVSZXF1aXJlZCI6dHJ1ZSwidGV4dHVyZXMiOnsiU0tJTiI6eyJ1cmwiOiJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2Q3Y2M2Njg3NDIzZDA1NzBkNTU2YWM1M2UwNjc2Y2I1NjNiYmRkOTcxN2NkODI2OWJkZWJlZDZmNmQ0ZTdiZjgifX19"); + arr.add("/craft:CRAFTING_TABLE"); + arr.add("/enderchest:ENDER_CHEST"); + arr.add("/wardrobe:LEATHER_CHESTPLATE"); + return arr; + } public List