aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorDeDiamondPro <67508414+DeDiamondPro@users.noreply.github.com>2021-12-29 15:26:46 +0100
committerGitHub <noreply@github.com>2021-12-29 09:26:46 -0500
commit98436ad0cf75c9a4bacbfb35a63948cc61c543f9 (patch)
treea06e98a175174b9e5aecc587499c46c8d1db394a /src/main
parentafee9dc68041cfc01e02508a86ba24bb899c29fc (diff)
downloadNotEnoughUpdates-98436ad0cf75c9a4bacbfb35a63948cc61c543f9.tar.gz
NotEnoughUpdates-98436ad0cf75c9a4bacbfb35a63948cc61c543f9.tar.bz2
NotEnoughUpdates-98436ad0cf75c9a4bacbfb35a63948cc61c543f9.zip
Add price graph (#43)
* graph thingy * styles * cool stuff * move enabled to top * no pretty printing so smaller files * general improvements to how data is visualised * small oopsie * bazaar support (delete old price data or crash) * Update AHGraph.java * remove 1 line so iron doesn't have to * Update GuiPriceGraph.java * happy now jani? * there ironmoon Co-authored-by: nopothegamer <40329022+nopothegamer@users.noreply.github.com>
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java4
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java3
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java10
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiPriceGraph.java492
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java7
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/AHGraph.java83
-rw-r--r--src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java75
-rw-r--r--src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui.pngbin0 -> 2079 bytes
-rw-r--r--src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_dark.pngbin0 -> 2088 bytes
-rw-r--r--src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_fsr.pngbin0 -> 2045 bytes
-rw-r--r--src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_phqdark.pngbin0 -> 2091 bytes
11 files changed, 673 insertions, 1 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java
index 18e0fc3b..acc15e8d 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUOverlay.java
@@ -17,6 +17,7 @@ import io.github.moulberry.notenoughupdates.mbgui.MBGuiElement;
import io.github.moulberry.notenoughupdates.mbgui.MBGuiGroupAligned;
import io.github.moulberry.notenoughupdates.mbgui.MBGuiGroupFloating;
import io.github.moulberry.notenoughupdates.miscfeatures.SunTzu;
+import io.github.moulberry.notenoughupdates.miscgui.GuiPriceGraph;
import io.github.moulberry.notenoughupdates.options.NEUConfigEditor;
import io.github.moulberry.notenoughupdates.util.Constants;
import io.github.moulberry.notenoughupdates.util.LerpingFloat;
@@ -1037,6 +1038,9 @@ public class NEUOverlay extends Gui {
textField.setText("id:" + internalname.get());
itemPaneOpen = true;
updateSearch();
+ } else if (keyPressed == NotEnoughUpdates.INSTANCE.config.ahGraph.graphKey && NotEnoughUpdates.INSTANCE.config.ahGraph.graphEnabled) {
+ NotEnoughUpdates.INSTANCE.openGui = new GuiPriceGraph(internalname.get());
+ return true;
}
}
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
index b3e145a8..254ab5cf 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java
@@ -6,6 +6,7 @@ import com.google.gson.JsonObject;
import io.github.moulberry.notenoughupdates.ItemPriceInformation;
import io.github.moulberry.notenoughupdates.NEUManager;
import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.miscgui.GuiPriceGraph;
import io.github.moulberry.notenoughupdates.util.Constants;
import io.github.moulberry.notenoughupdates.util.Utils;
import net.minecraft.client.Minecraft;
@@ -261,6 +262,7 @@ public class APIManager {
ItemPriceInformation.updateAuctionableItemsList();
didFirstUpdate = true;
}
+ GuiPriceGraph.addToCache(lowestBins, false);
}, () -> {});
}
@@ -675,6 +677,7 @@ public class APIManager {
bazaarJson.add(entry.getKey().replace(":", "-"), productInfo);
}
}
+ GuiPriceGraph.addToCache(bazaarJson, true);
});
}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java
index 97004752..910870d5 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/Commands.java
@@ -715,7 +715,7 @@ public class Commands {
"Ok, this is actually the last message, use the command again and you'll crash I promise"};
private int devFailIndex = 0;
- private static final List<String> devTestUsers = new ArrayList<>(Arrays.asList("moulberry", "lucycoconut", "ironm00n", "ariyio", "throwpo"));
+ private static final List<String> devTestUsers = new ArrayList<>(Arrays.asList("moulberry", "lucycoconut", "ironm00n", "ariyio", "throwpo", "dediamondpro"));
SimpleCommand devTestCommand = new SimpleCommand("neudevtest", new SimpleCommand.ProcessCommandRunnable() {
@Override
public void processCommand(ICommandSender sender, String[] args) {
@@ -748,6 +748,14 @@ public class Commands {
DupePOC.doDupe(args[0]);
return;
}*/
+ if (args.length >= 1 && args[0].equalsIgnoreCase("pricetest")) {
+ if (args.length == 1) {
+ NotEnoughUpdates.INSTANCE.manager.auctionManager.updateBazaar();
+ } else {
+ NotEnoughUpdates.INSTANCE.openGui = new GuiPriceGraph(args[1]);
+ }
+ }
+
if (args.length == 1 && args[0].equalsIgnoreCase("positiontest")) {
NotEnoughUpdates.INSTANCE.openGui = new GuiPositionEditor();
return;
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiPriceGraph.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiPriceGraph.java
new file mode 100644
index 00000000..a063bafe
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/GuiPriceGraph.java
@@ -0,0 +1,492 @@
+package io.github.moulberry.notenoughupdates.miscgui;
+
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import io.github.moulberry.notenoughupdates.NotEnoughUpdates;
+import io.github.moulberry.notenoughupdates.util.SpecialColour;
+import io.github.moulberry.notenoughupdates.util.Utils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumChatFormatting;
+import net.minecraft.util.ResourceLocation;
+import org.lwjgl.opengl.GL11;
+
+import java.io.*;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+public class GuiPriceGraph extends GuiScreen {
+
+ private static final Gson GSON = new GsonBuilder().create();
+ private static final SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
+ private final ResourceLocation TEXTURE;
+ private static final int X_SIZE = 364;
+ private static final int Y_SIZE = 215;
+ private Data dataPoints;
+ private float highestValue;
+ private long firstTime;
+ private long lastTime;
+ private Float lowestValue = null;
+ private String itemName;
+ private final String itemId;
+ private int guiLeft;
+ private int guiTop;
+ private ItemStack itemStack = null;
+ private boolean loaded = false;
+ /**
+ * 0 = hour
+ * 1 = day
+ * 2 = week
+ * 3 = all
+ * 4 = custom
+ **/
+ private int mode = NotEnoughUpdates.INSTANCE.config.ahGraph.defaultMode;
+ private long customStart = 0;
+ private long customEnd = 0;
+ private boolean customSelecting = false;
+
+ public GuiPriceGraph(String itemId) {
+ switch (NotEnoughUpdates.INSTANCE.config.ahGraph.graphStyle) {
+ case 1:
+ TEXTURE = new ResourceLocation("notenoughupdates:price_graph_gui/price_information_gui_dark.png");
+ break;
+ case 2:
+ TEXTURE = new ResourceLocation("notenoughupdates:price_graph_gui/price_information_gui_phqdark.png");
+ break;
+ case 3:
+ TEXTURE = new ResourceLocation("notenoughupdates:price_graph_gui/price_information_gui_fsr.png");
+ break;
+ default:
+ TEXTURE = new ResourceLocation("notenoughupdates:price_graph_gui/price_information_gui.png");
+ break;
+ }
+ this.itemId = itemId;
+ if (NotEnoughUpdates.INSTANCE.manager.getItemInformation().containsKey(itemId)) {
+ JsonObject itemInfo = NotEnoughUpdates.INSTANCE.manager.getItemInformation().get(itemId);
+ itemName = itemInfo.get("displayname").getAsString();
+ itemStack = NotEnoughUpdates.INSTANCE.manager.jsonToStack(itemInfo);
+ }
+ loadData();
+ }
+
+ @Override
+ public void drawScreen(int mouseX, int mouseY, float partialTicks) {
+ guiLeft = (width - X_SIZE) / 2;
+ guiTop = (height - Y_SIZE) / 2;
+
+ Minecraft.getMinecraft().getTextureManager().bindTexture(TEXTURE);
+ GlStateManager.color(1, 1, 1, 1);
+ Utils.drawTexturedRect(guiLeft, guiTop, X_SIZE, Y_SIZE,
+ 0, X_SIZE / 512f, 0, Y_SIZE / 512f, GL11.GL_NEAREST);
+ Utils.drawTexturedRect(guiLeft + 245, guiTop + 17, 16, 16,
+ 0, 16 / 512f, (mode == 0 ? 215 : 231) / 512f, (mode == 0 ? 231 : 247) / 512f, GL11.GL_NEAREST);
+ Utils.drawTexturedRect(guiLeft + 263, guiTop + 17, 16, 16,
+ 16 / 512f, 32 / 512f, (mode == 1 ? 215 : 231) / 512f, (mode == 1 ? 231 : 247) / 512f, GL11.GL_NEAREST);
+ Utils.drawTexturedRect(guiLeft + 281, guiTop + 17, 16, 16,
+ 32 / 512f, 48 / 512f, (mode == 2 ? 215 : 231) / 512f, (mode == 2 ? 231 : 247) / 512f, GL11.GL_NEAREST);
+ Utils.drawTexturedRect(guiLeft + 299, guiTop + 17, 16, 16,
+ 48 / 512f, 64 / 512f, (mode == 3 ? 215 : 231) / 512f, (mode == 3 ? 231 : 247) / 512f, GL11.GL_NEAREST);
+
+ if (itemName != null && itemStack != null) {
+ Utils.drawItemStack(itemStack, guiLeft + 16, guiTop + 11);
+ Utils.drawStringScaledMax(itemName, Minecraft.getMinecraft().fontRendererObj, guiLeft + 35, guiTop + 13, false,
+ 0xffffff, 1.77f, 208);
+ }
+
+ if (!loaded)
+ Utils.drawStringCentered("Loading...", Minecraft.getMinecraft().fontRendererObj,
+ guiLeft + 166, guiTop + 116, false, 0xffffff00);
+ else if (dataPoints == null || dataPoints.get() == null || dataPoints.get().size() <= 1)
+ Utils.drawStringCentered("No data found.", Minecraft.getMinecraft().fontRendererObj,
+ guiLeft + 166, guiTop + 116, false, 0xffff0000);
+ else {
+
+ int graphColor = SpecialColour.specialToChromaRGB(NotEnoughUpdates.INSTANCE.config.ahGraph.graphColor);
+ int graphColor2 = SpecialColour.specialToChromaRGB(NotEnoughUpdates.INSTANCE.config.ahGraph.graphColor2);
+ Integer lowestDist = null;
+ Long lowestDistTime = null;
+ HashMap<Integer, Integer> secondLineData = new HashMap<>();
+ for (int i = (dataPoints.isBz() ? 1 : 0); i >= 0; i--) {
+ Utils.drawGradientRect(0, guiLeft + 17, guiTop + 35, guiLeft + 315, guiTop + 198,
+ changeAlpha(i == 0 ? graphColor : graphColor2, 120), changeAlpha(i == 0 ? graphColor : graphColor2, 10));
+ Integer prevX = null;
+ Integer prevY = null;
+ for (Long time : dataPoints.get().keySet()) {
+ float price = dataPoints.isBz() ? i == 0 ? dataPoints.bz.get(time).b : dataPoints.bz.get(time).s : dataPoints.ah.get(time);
+ int xPos = (int) map(time, firstTime, lastTime, guiLeft + 17, guiLeft + 315);
+ int yPos = (int) map(price, highestValue + 10, lowestValue - 10, guiTop + 35, guiTop + 198);
+ if (prevX != null) {
+ Minecraft.getMinecraft().getTextureManager().bindTexture(TEXTURE);
+ GlStateManager.color(1, 1, 1, 1);
+ Utils.drawTexturedQuad(prevX, prevY, xPos, yPos, xPos, guiTop + 35, prevX, guiTop + 35, 18 / 512f, 19 / 512f,
+ 36 / 512f, 37 / 512f, GL11.GL_NEAREST);
+ if (i == 0) {
+ Utils.drawLine(prevX, prevY + 0.5f, xPos, yPos + 0.5f, 2, graphColor);
+ if (dataPoints.isBz())
+ Utils.drawLine(prevX, secondLineData.get(prevX) + 0.5f, xPos, secondLineData.get(xPos) + 0.5f, 2, graphColor2);
+ }
+ }
+ if (i == 1)
+ secondLineData.put(xPos, yPos);
+ if (mouseX >= guiLeft + 17 && mouseX <= guiLeft + 315 && mouseY >= guiTop + 35 && mouseY <= guiTop + 198) {
+ int dist = Math.abs(mouseX - xPos);
+ if (lowestDist == null || dist < lowestDist) {
+ lowestDist = dist;
+ lowestDistTime = time;
+ }
+ }
+ prevX = xPos;
+ prevY = yPos;
+ }
+ }
+ boolean showDays = lastTime - firstTime > 86400;
+ int prevNum = showDays ? Date.from(Instant.ofEpochSecond(firstTime)).getDate() : Date.from(Instant.ofEpochSecond(firstTime)).getHours();
+ long prevXPos = -100;
+ for (long time = firstTime; time <= lastTime; time += showDays ? 3600 : 60) {
+ int num = showDays ? Date.from(Instant.ofEpochSecond(time)).getDate() : Date.from(Instant.ofEpochSecond(time)).getHours();
+ if (num != prevNum) {
+ int xPos = (int) map(time, firstTime, lastTime, guiLeft + 17, guiLeft + 315);
+ if (Math.abs(prevXPos - xPos) > 30) {
+ Utils.drawStringCentered(String.valueOf(num), Minecraft.getMinecraft().fontRendererObj,
+ xPos, guiTop + 206, false, 0x8b8b8b);
+ prevXPos = xPos;
+ }
+ prevNum = num;
+ }
+ }
+ for (int i = 0; i <= 6; i++) {
+ long price = (long) map(i, 0, 6, highestValue, lowestValue);
+ String formattedPrice = formatPrice(price);
+ Utils.drawStringF(formattedPrice, Minecraft.getMinecraft().fontRendererObj, guiLeft + 320,
+ (float) map(i, 0, 6, guiTop + 35, guiTop + 198)
+ - Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT / 2f,
+ false, 0x8b8b8b);
+ }
+ if (customSelecting) {
+ Utils.drawDottedLine(customStart, guiTop + 36, customStart, guiTop + 197, 2, 10, 0xFFc6c6c6);
+ Utils.drawDottedLine(customEnd, guiTop + 36, customEnd, guiTop + 197, 2, 10, 0xFFc6c6c6);
+ Utils.drawDottedLine(customStart, guiTop + 36, customEnd, guiTop + 36, 2, 10, 0xFFc6c6c6);
+ Utils.drawDottedLine(customStart, guiTop + 197, customEnd, guiTop + 197, 2, 10, 0xFFc6c6c6);
+ }
+ if (lowestDist != null && !customSelecting) {
+ float price = dataPoints.isBz() ? dataPoints.bz.get(lowestDistTime).b : dataPoints.ah.get(lowestDistTime);
+ Float price2 = dataPoints.isBz() ? dataPoints.bz.get(lowestDistTime).s : null;
+ int xPos = (int) map(lowestDistTime, firstTime, lastTime, guiLeft + 17, guiLeft + 315);
+ int yPos = (int) map(price, highestValue + 10, lowestValue - 10, guiTop + 35, guiTop + 198);
+ int yPos2 = price2 != null ? (int) map(price2, highestValue + 10, lowestValue - 10, guiTop + 35, guiTop + 198) : 0;
+
+ Utils.drawLine(xPos, guiTop + 35, xPos, guiTop + 198, 2, 0x4D8b8b8b);
+ Minecraft.getMinecraft().getTextureManager().bindTexture(TEXTURE);
+ GlStateManager.color(1, 1, 1, 1);
+ Utils.drawTexturedRect(xPos - 2.5f, yPos - 2.5f, 5, 5,
+ 0, 5 / 512f, 247 / 512f, 252 / 512f, GL11.GL_NEAREST);
+ if (price2 != null) {
+ Utils.drawTexturedRect(xPos - 2.5f, yPos2 - 2.5f, 5, 5,
+ 0, 5 / 512f, 247 / 512f, 252 / 512f, GL11.GL_NEAREST);
+ }
+
+ Date date = Date.from(Instant.ofEpochSecond(lowestDistTime));
+ SimpleDateFormat displayFormat = new SimpleDateFormat("'§b'd MMMMM yyyy '§eat§b' HH:mm");
+ NumberFormat nf = NumberFormat.getInstance();
+ ArrayList<String> text = new ArrayList<>();
+ text.add(displayFormat.format(date));
+ if (dataPoints.isBz()) {
+ text.add(EnumChatFormatting.YELLOW + "" + EnumChatFormatting.BOLD + "Bazaar Insta-Buy: " +
+ EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + nf.format(price));
+ text.add(EnumChatFormatting.YELLOW + "" + EnumChatFormatting.BOLD + "Bazaar Insta-Sell: " +
+ EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + nf.format(price2));
+ } else {
+ text.add(EnumChatFormatting.YELLOW + "" + EnumChatFormatting.BOLD + "Lowest BIN: " +
+ EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + nf.format(price));
+ }
+ drawHoveringText(text, xPos, yPos);
+ }
+ }
+
+ if (mouseY >= guiTop + 17 && mouseY <= guiTop + 35 && mouseX >= guiLeft + 244 && mouseX <= guiLeft + 316) {
+ int index = (mouseX - guiLeft - 245) / 18;
+ switch (index) {
+ case 0:
+ Gui.drawRect(guiLeft + 245, guiTop + 17, guiLeft + 261, guiTop + 33, 0x80ffffff);
+ drawHoveringText(Collections.singletonList("Show 1 Hour"), mouseX, mouseY);
+ break;
+ case 1:
+ Gui.drawRect(guiLeft + 263, guiTop + 17, guiLeft + 279, guiTop + 33, 0x80ffffff);
+ drawHoveringText(Collections.singletonList("Show 1 Day"), mouseX, mouseY);
+ break;
+ case 2:
+ Gui.drawRect(guiLeft + 281, guiTop + 17, guiLeft + 297, guiTop + 33, 0x80ffffff);
+ drawHoveringText(Collections.singletonList("Show 1 Week"), mouseX, mouseY);
+ break;
+ case 3:
+ Gui.drawRect(guiLeft + 299, guiTop + 17, guiLeft + 315, guiTop + 33, 0x80ffffff);
+ drawHoveringText(Collections.singletonList("Show All"), mouseX, mouseY);
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
+ super.mouseClicked(mouseX, mouseY, mouseButton);
+ if (mouseY >= guiTop + 17 && mouseY <= guiTop + 35 && mouseX >= guiLeft + 244 && mouseX <= guiLeft + 316) {
+ mode = (mouseX - guiLeft - 245) / 18;
+ loadData();
+ } else if (mouseY >= guiTop + 35 && mouseY <= guiTop + 198 && mouseX >= guiLeft + 17 && mouseX <= guiLeft + 315) {
+ customSelecting = true;
+ customStart = mouseX;
+ customEnd = mouseX;
+ }
+ }
+
+ @Override
+ protected void mouseReleased(int mouseX, int mouseY, int state) {
+ super.mouseReleased(mouseX, mouseY, state);
+ if (customSelecting) {
+ customSelecting = false;
+ customStart = (int) map(customStart, guiLeft + 17, guiLeft + 315, firstTime, lastTime);
+ customEnd = (int) map(mouseX, guiLeft + 17, guiLeft + 315, firstTime, lastTime);
+ if (customStart > customEnd) {
+ long temp = customStart;
+ customStart = customEnd;
+ customEnd = temp;
+ }
+ if (customEnd - customStart != 0) {
+ mode = 4;
+ loadData();
+ }
+ }
+ }
+
+ @Override
+ protected void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) {
+ super.mouseClickMove(mouseX, mouseY, clickedMouseButton, timeSinceLastClick);
+ if (customSelecting) {
+ customEnd = mouseX < guiLeft + 18 ? guiLeft + 18 : Math.min(mouseX, guiLeft + 314);
+ }
+ }
+
+ private void loadData() {
+ dataPoints = null;
+ loaded = false;
+ new Thread(() -> {
+ File dir = new File("config/notenoughupdates/prices");
+ if (!dir.exists()) {
+ loaded = true;
+ return;
+ }
+ File[] files = dir.listFiles();
+ Data data = new Data();
+ if (files == null) return;
+ for (File file : files) {
+ if (!file.getName().endsWith(".gz")) continue;
+ HashMap<String, Data> data2 = load(file);
+ if (data2 == null || !data2.containsKey(itemId)) continue;
+ if (data2.get(itemId).isBz()) {
+ if (data.bz == null) data.bz = data2.get(itemId).bz;
+ else data.bz.putAll(data2.get(itemId).bz);
+ } else if (data.ah == null) data.ah = data2.get(itemId).ah;
+ else data.ah.putAll(data2.get(itemId).ah);
+ }
+ if (data.get() != null && !data.get().isEmpty()) {
+ if (mode < 3)
+ data = new Data(new TreeMap<>(data.get().entrySet().stream()
+ .filter(e -> e.getKey() > System.currentTimeMillis() / 1000 - (mode == 0 ? 3600 : mode == 1 ? 86400 : 604800))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))), data.isBz());
+ else if (mode == 4)
+ data = new Data(new TreeMap<>(data.get().entrySet().stream()
+ .filter(e -> e.getKey() >= customStart && e.getKey() <= customEnd)
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))), data.isBz());
+ if (data.get() == null || data.get().isEmpty()) {
+ loaded = true;
+ return;
+ }
+ dataPoints = trimData(data);
+ firstTime = dataPoints.get().firstKey();
+ lastTime = dataPoints.get().lastKey();
+ highestValue = 0;
+ lowestValue = null;
+ for (long key : dataPoints.get().keySet()) {
+ float value1 = dataPoints.isBz() ? dataPoints.bz.get(key).b : dataPoints.ah.get(key);
+ Float value2 = dataPoints.isBz() ? dataPoints.bz.get(key).s : null;
+ if (value1 > highestValue) {
+ highestValue = value1;
+ }
+ if (value2 != null && value2 > highestValue) {
+ highestValue = value2;
+ }
+ if (lowestValue == null || value1 < lowestValue) {
+ lowestValue = value1;
+ }
+ if (value2 != null && value2 < lowestValue) {
+ lowestValue = value2;
+ }
+ }
+ }
+ loaded = true;
+ }).start();
+ }
+
+ public static void addToCache(JsonObject items, boolean bazaar) {
+ if (!NotEnoughUpdates.INSTANCE.config.ahGraph.graphEnabled) return;
+ try {
+ File dir = new File("config/notenoughupdates/prices");
+ if (!dir.exists() && !dir.mkdir()) return;
+ File[] files = dir.listFiles();
+ if (files != null)
+ for (File file : files) {
+ if (!file.getName().endsWith(".gz")) continue;
+ if (file.lastModified() < System.currentTimeMillis() - NotEnoughUpdates.INSTANCE.config.ahGraph.dataRetention * 86400000L)
+ file.delete();
+ }
+ Date date = new Date();
+ Long epochSecond = date.toInstant().getEpochSecond();
+ File file = new File(dir, "prices_" + format.format(date) + ".gz");
+ HashMap<String, Data> prices = new HashMap<>();
+ if (file.exists())
+ prices = load(file);
+ if (prices == null) return;
+ for (Map.Entry<String, JsonElement> item : items.entrySet()) {
+ if (prices.containsKey(item.getKey())) {
+ if (bazaar && item.getValue().getAsJsonObject().has("curr_buy") && item.getValue().getAsJsonObject().has("curr_sell"))
+ prices.get(item.getKey()).bz.put(epochSecond, new BzData(item.getValue().getAsJsonObject().get("curr_buy").getAsFloat(),
+ item.getValue().getAsJsonObject().get("curr_sell").getAsFloat()));
+ else if (!bazaar)
+ prices.get(item.getKey()).ah.put(epochSecond, item.getValue().getAsInt());
+ } else {
+ TreeMap<Long, Object> mapData = new TreeMap<>();
+ if (bazaar && item.getValue().getAsJsonObject().has("curr_buy") && item.getValue().getAsJsonObject().has("curr_sell"))
+ mapData.put(epochSecond, new BzData(item.getValue().getAsJsonObject().get("curr_buy").getAsFloat(),
+ item.getValue().getAsJsonObject().get("curr_sell").getAsFloat()));
+ else if (!bazaar)
+ mapData.put(epochSecond, item.getValue().getAsLong());
+ prices.put(item.getKey(), new Data(mapData, bazaar));
+ }
+ }
+ //noinspection ResultOfMethodCallIgnored
+ file.createNewFile();
+ try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(file)), StandardCharsets.UTF_8))) {
+ writer.write(GSON.toJson(prices));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Data trimData(Data data) {
+ long first = data.get().firstKey();
+ long last = data.get().lastKey();
+ Data trimmed = new Data();
+ if (data.isBz())
+ trimmed.bz = new TreeMap<>();
+ else
+ trimmed.ah = new TreeMap<>();
+ int zones = NotEnoughUpdates.INSTANCE.config.ahGraph.graphZones;
+ Long[] dataArray = !data.isBz() ? data.ah.keySet().toArray(new Long[0]) : data.bz.keySet().toArray(new Long[0]);
+ int prev = 0;
+ for (int i = 0; i < zones; i++) {
+ long lowest = (long) map(i, 0, zones, first, last);
+ long highest = (long) map(i + 1, 0, zones, first, last);
+ int amount = 0;
+ double sumBuy = 0;
+ double sumSell = 0;
+ for (int l = prev; l < dataArray.length; l++) {
+ if (dataArray[l] >= lowest && dataArray[l] <= highest) {
+ amount++;
+ sumBuy += data.isBz() ? data.bz.get(dataArray[l]).b : data.ah.get(dataArray[l]);
+ if (data.isBz()) sumSell += data.bz.get(dataArray[l]).s;
+ prev = l + 1;
+ } else if (dataArray[l] > highest)
+ break;
+ }
+ if (amount > 0) {
+ if (data.isBz())
+ trimmed.bz.put(lowest, new BzData((float) (sumBuy / amount), (float) (sumSell / amount)));
+ else
+ trimmed.ah.put(lowest, (int) (sumBuy / amount));
+ }
+ }
+ return trimmed;
+ }
+
+
+ private static HashMap<String, Data> load(File file) {
+ Type type = new TypeToken<HashMap<String, Data>>() {
+ }.getType();
+ if (file.exists()) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(file)), StandardCharsets.UTF_8))) {
+ return GSON.fromJson(reader, type);
+ } catch (Exception ignored) {
+ }
+ }
+ return null;
+ }
+
+ private static double map(double x, double in_min, double in_max, double out_min, double out_max) {
+ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+ }
+
+ private static String formatPrice(long price) {
+ DecimalFormat df = new DecimalFormat("#.00");
+ if (price >= 1000000000) {
+ return df.format(price / 1000000000f) + "B";
+ } else if (price >= 1000000) {
+ return df.format(price / 1000000f) + "M";
+ } else if (price >= 1000) {
+ return df.format(price / 1000f) + "K";
+ }
+ return String.valueOf(price);
+ }
+
+ private int changeAlpha(int origColor, int alpha) {
+ origColor = origColor & 0x00ffffff; //drop the previous alpha value
+ return (alpha << 24) | origColor; //add the one the user inputted
+ }
+}
+
+class Data {
+ public TreeMap<Long, Integer> ah = null;
+ public TreeMap<Long, BzData> bz = null;
+
+ public Data() {
+ }
+
+ public Data(TreeMap<Long, ?> map, boolean bz) {
+ if (bz)
+ this.bz = (TreeMap<Long, BzData>) map;
+ else
+ this.ah = (TreeMap<Long, Integer>) map;
+ }
+
+ public TreeMap<Long, ?> get() {
+ return !isBz() ? ah : bz;
+ }
+
+ public boolean isBz() {
+ return bz != null && !bz.isEmpty();
+ }
+}
+
+class BzData {
+ float b;
+ float s;
+
+ public BzData(float b, float s) {
+ this.b = b;
+ this.s = s;
+ }
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
index 31930e77..1cb06c52 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/NEUConfig.java
@@ -273,6 +273,13 @@ public class NEUConfig extends Config {
@Expose
@Category(
+ name = "AH/BZ Graph",
+ desc = "Graph of auction and bazaar prices"
+ )
+ public AHGraph ahGraph = new AHGraph();
+
+ @Expose
+ @Category(
name = "Accessory Bag Overlay",
desc = "Accessory Bag Overlay"
)
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/AHGraph.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/AHGraph.java
new file mode 100644
index 00000000..ce4c8f09
--- /dev/null
+++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/AHGraph.java
@@ -0,0 +1,83 @@
+package io.github.moulberry.notenoughupdates.options.seperateSections;
+
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.notenoughupdates.core.config.annotations.*;
+import org.lwjgl.input.Keyboard;
+
+public class AHGraph {
+ @Expose
+ @ConfigOption(
+ name = "Enabled",
+ desc = "Enable or disable the graph. Disabling this will also make it so that no price data is stored."
+ )
+ @ConfigEditorBoolean
+ public boolean graphEnabled = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Keybind",
+ desc = "Key to press to open the graph."
+ )
+ @ConfigEditorKeybind(defaultKey = Keyboard.KEY_P)
+ public int graphKey = Keyboard.KEY_P;
+
+ @Expose
+ @ConfigOption(
+ name = "GUI Style",
+ desc = "Change the style of the graph GUI"
+ )
+ @ConfigEditorDropdown(
+ values = {"Minecraft", "Dark", "PacksHQ Dark", "FSR"}
+ )
+ public int graphStyle = 0;
+
+ @Expose
+ @ConfigOption(
+ name = "Graph Colour",
+ desc = "Set a custom colour for the graph."
+ )
+ @ConfigEditorColour
+ public String graphColor = "0:255:0:255:0";
+
+ @Expose
+ @ConfigOption(
+ name = "Secondary Graph Colour",
+ desc = "Set a custom colour for the second graph line."
+ )
+ @ConfigEditorColour
+ public String graphColor2 = "0:255:255:255:0";
+
+ @Expose
+ @ConfigOption(
+ name = "Default Time",
+ desc = "Change the default time period for the graph."
+ )
+ @ConfigEditorDropdown(
+ values = {"1 Hour", "1 Day", "1 Week", "All Time"}
+ )
+ public int defaultMode = 1;
+
+ @Expose
+ @ConfigOption(
+ name = "Data Retention",
+ desc = "Change the time (in days) that data is kept for.\nLonger retention require more storage."
+ )
+ @ConfigEditorSlider(
+ minValue = 1,
+ maxValue = 30,
+ minStep = 1
+ )
+ public int dataRetention = 7;
+
+ @Expose
+ @ConfigOption(
+ name = "Number of Graph Zones",
+ desc = "Change the number of graph zones.\nHigher numbers will have more detail, but will look way more cramped."
+ )
+ @ConfigEditorSlider(
+ minValue = 50,
+ maxValue = 300,
+ minStep = 1
+ )
+ public int graphZones = 175;
+}
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
index f34ba230..dc301db2 100644
--- a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
+++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java
@@ -472,6 +472,7 @@ public class Utils {
//EnumChatFormatting.AQUA+EnumChatFormatting.BOLD.toString()+"DIVINE",
};
+
public static final HashMap<String, String> rarityArrMap = new HashMap<String, String>() {{
put("COMMON", rarityArrC[0]);
put("UNCOMMON", rarityArrC[1]);
@@ -828,6 +829,16 @@ public class Utils {
GlStateManager.scale(1 / factor, 1 / factor, 1);
}
+ public static void drawStringScaledMax(String str, FontRenderer fr, float x, float y, boolean shadow, int colour, float factor, int len) {
+ int strLen = fr.getStringWidth(str);
+ float f = len / (float) strLen;
+ factor = Math.min(factor, f);
+
+ GlStateManager.scale(factor, factor, 1);
+ fr.drawString(str, x / factor, y / factor, colour, shadow);
+ GlStateManager.scale(1 / factor, 1 / factor, 1);
+ }
+
public static void drawStringCenteredScaledMaxWidth(String str, FontRenderer fr, float x, float y, boolean shadow, int len, int colour) {
int strLen = fr.getStringWidth(str);
float factor = len / (float) strLen;
@@ -1351,4 +1362,68 @@ public class Utils {
return endsIn;
}
+
+ public static void drawLine(float sx, float sy, float ex, float ey, int width, int color) {
+ float f = (float) (color >> 24 & 255) / 255.0F;
+ float f1 = (float) (color >> 16 & 255) / 255.0F;
+ float f2 = (float) (color >> 8 & 255) / 255.0F;
+ float f3 = (float) (color & 255) / 255.0F;
+ GlStateManager.pushMatrix();
+ GlStateManager.disableTexture2D();
+ GlStateManager.enableBlend();
+ GlStateManager.disableAlpha();
+ GlStateManager.tryBlendFuncSeparate(770, 771, 1, 0);
+ GlStateManager.color(f1, f2, f3, f);
+ GL11.glLineWidth(width);
+ GL11.glBegin(GL11.GL_LINES);
+ GL11.glVertex2d(sx, sy);
+ GL11.glVertex2d(ex, ey);
+ GL11.glEnd();
+ GlStateManager.disableBlend();
+ GlStateManager.enableAlpha();
+ GlStateManager.enableTexture2D();
+ GlStateManager.popMatrix();
+ }
+
+ public static void drawDottedLine(float sx, float sy, float ex, float ey, int width, int factor , int color) {
+ GlStateManager.pushMatrix();
+ GL11.glLineStipple(factor, (short) 0xAAAA);
+ GL11.glEnable(GL11.GL_LINE_STIPPLE);
+ drawLine(sx, sy, ex, ey, width, color);
+ GL11.glDisable(GL11.GL_LINE_STIPPLE);
+ GlStateManager.popMatrix();
+ }
+
+ public static void drawTexturedQuad(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4,
+ float uMin, float uMax, float vMin, float vMax, int filter) {
+ GlStateManager.enableTexture2D();
+ GlStateManager.enableBlend();
+ GlStateManager.tryBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ GL14.glBlendFuncSeparate(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter);
+
+ Tessellator tessellator = Tessellator.getInstance();
+ WorldRenderer worldrenderer = tessellator.getWorldRenderer();
+ worldrenderer.begin(7, DefaultVertexFormats.POSITION_TEX);
+ worldrenderer
+ .pos(x1, y1, 0.0D)
+ .tex(uMin, vMax).endVertex();
+ worldrenderer
+ .pos(x2, y2, 0.0D)
+ .tex(uMax, vMax).endVertex();
+ worldrenderer
+ .pos(x3, y3, 0.0D)
+ .tex(uMax, vMin).endVertex();
+ worldrenderer
+ .pos(x4, y4, 0.0D)
+ .tex(uMin, vMin).endVertex();
+ tessellator.draw();
+
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
+ GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
+
+ GlStateManager.disableBlend();
+ }
}
diff --git a/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui.png b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui.png
new file mode 100644
index 00000000..633be870
--- /dev/null
+++ b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui.png
Binary files differ
diff --git a/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_dark.png b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_dark.png
new file mode 100644
index 00000000..0f7743e6
--- /dev/null
+++ b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_dark.png
Binary files differ
diff --git a/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_fsr.png b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_fsr.png
new file mode 100644
index 00000000..e98bdb7d
--- /dev/null
+++ b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_fsr.png
Binary files differ
diff --git a/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_phqdark.png b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_phqdark.png
new file mode 100644
index 00000000..f794319f
--- /dev/null
+++ b/src/main/resources/assets/notenoughupdates/price_graph_gui/price_information_gui_phqdark.png
Binary files differ