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 + 17 files changed, 1249 insertions(+), 627 deletions(-) (limited to 'src/main/java') 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().f